function elcookie(name, value, opts) {
	if (typeof value == 'undefined') {
		if (document.cookie && document.cookie != '') {
			var cookies = document.cookie.split(';');
			var test = name+'=';
			for (var i=0; i<cookies.length; i++) {
				var c = $.trim(cookies[i]);
				if (c.substring(0, name.length+1) == test) {
					return decodeURIComponent(c.substring(name.length+1));
				}
			}
		}
		return '';
	} else {
		opts = $.extend({expires : '', path : '', domain : '', secure : false}, opts);
		if (value===null) {
			value = '';
			opts.expires = -1;
		}
		var expires = '';
		if (opts.expires) {
			var d = opts.expires.toUTCString ? opts.expires : new Date();
			if (typeof opts.expires == 'number') {
				d.setTime(d.getTime() + (opts.expires * 24 * 60 * 60 * 1000));
			}
			expires = '; expires='+d.toUTCString();
		}
		document.cookie = name+'='+encodeURIComponent(value)+expires+(opts.path ? '; path='+opts.path : '')+(opts.domain ? '; domain='+opts.domain : '')+(opts.secure ? '; secure' : '');
	}
}
/**
 * @class eli18n
 * Javascript applications localization 
 *
 * @param Object o - class options. Object. {textdomain : 'Ð¸Ð¼Ñ_Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹_ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ð¹', messages : {textdomain1 : {}[, textdomain2 : {}]...}}
 *
 * Usage:
 * 
 * var msgs = { Hello : 'ÐŸÑ€ÐµÐ²ÑÐ´', 'Hello %user' : 'ÐŸÑ€ÐµÐ²ÐµÐ´ %user' };
 * //load messages and set default textdomain
 * var translator = new eli18n( {textdomain : 'test', messages : {test : msgs}} )
 * window.console.log(translator.translate('Hello'));
 * window.console.log(translator.format('Hello %user', {user : 'David Blain'}))
 * // create new textdomain
 * translator.load({test2 : {'Goodbye' : 'Ja, deva mata!'} })
 * // and use it, without changing default one
 * window.console.log(translator.translate('Goodbye', 'test2'));
 *
 * @author:    Dmitry (dio) Levashov dio@std42.ru
 * license:   BSD license
 **/
function eli18n(o) {
	
	/**
	 * Get/set default textdomain
	 *
	 * @param String d new textdomain name
	 * @return String  default textdomain
	 **/
	this.textdomain = function(d) {
		return this.messages[d] ? this._domain = d : this._domain;
	}
	
	o && o.messages   && this.load(o.messages);
	o && o.textdomain && this.textdomain(o.textdomain);
}

eli18n.prototype = new function() {
	
	/**
	 * @var Object messages (key - messages in English or message handler, value - message in selected language)
	 **/
	this.messages = {};
	/**
	 * @var String default textdomain
	 **/
	this._domain   = '';
	
	/**
	 * Load new messages
	 *
	 * @param Object msgs - messages (key - textdomain name, value - messages Object)
	 * @return Object this
	 **/
	this.load = function(msgs) {
		if (typeof(msgs) == 'object') {
			for (var d in msgs) {
				var _msgs = msgs[d];
				if (typeof(_msgs) == 'object') {
					if (!this.messages[d]) {
						this.messages[d] = {}; 
					}
					for (var k in _msgs) {
						if (typeof(_msgs[k]) == 'string') {
							this.messages[d][k] = _msgs[k];
						}
					}
				}
			}
		}
		return this;
	}

	/**
	 * Return translated message, if message exists in required or default textdomain, otherwise returns original message
	 *
	 * @param  String msg - message
	 * @param  String d - textdomain. If empty, default textdomain will be used
	 * @return String translated message
	 **/
	this.translate = function(msg, d) {
		var d = d && this.messages[d] ? d : this._domain;
		return this.messages[d] && this.messages[d][msg] ? this.messages[d][msg] : msg;
		
	}
	
	/**
	 * Translate message and replace placeholders (%placeholder)
	 *
	 * @param  String  msg - message
	 * @param  Object  replacement for placeholders (keys - placeholders name without leading %, values - replacements)
	 * @param  String  d - textdomain. If empty, default textdomain will be used
	 * @return String  translated message
	 **/
	this.format = function(msg, data, d) {
		msg = this.translate(msg, d);
		if (typeof(data) == 'object') {
			for (var i in data) {
				msg = msg.replace('%'+i, this.translate(data[i], d));
			}
		}
		return msg;
	}
}
/**
 * @class elDialogForm
 * Wraper for jquery.ui.dialog and jquery.ui.tabs
 *  Create form in dialog. You can decorate it as you wish - with tabs or/and tables
 *
 * Usage:
 *   var d = new elDialogForm(opts)
 *   d.append(['Field name: ', $('<input type="text" name="f1" />')])
 *		.separator()
 *		.append(['Another field name: ', $('<input type="text" name="f2" />')])
 *      .open()
 * will create dialog with pair text field separated by horizontal rule
 * Calling append() with 2 additional arguments ( d.append([..], null, true)) 
 *  - will create table in dialog and put text inputs and labels in table cells
 *
 * Dialog with tabs:
 *   var d = new elDialogForm(opts)
 *   d.tab('first', 'First tab label)
 * 	  .tab('second', 'Second tab label)
 *    .append(['Field name: ', $('<input type="text" name="f1" />')], 'first', true)  - add label and input to first tab in table (table will create automagicaly)
 *    .append(['Field name 2: ', $('<input type="text" name="f2" />')], 'second', true)  - same in secon tab
 *
 * Options:
 *   class     - css class for dialog
 *   submit    - form submit event callback. Accept 2 args - event and this object
 *   ajaxForm  - arguments for ajaxForm, if needed (dont forget include jquery.form.js)
 *   tabs      - arguments for ui.tabs
 *   dialog    - arguments for ui.dialog
 *   name      - hidden text field in wich selected value will saved
 *
 * Notice!
 * When close dialog, it will destroing insead of dialog('close'). Reason - strange bug with tabs in dialog on secondary opening. 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 *
 **/
function elDialogForm(o) {
	var self = this;
	
	var defaults = {
		'class'   : 'el-dialogform',
		submit    : function(e, d) { window.console && window.console.log && window.console.log('submit called'); d.close(); },
		form      : { action : window.location.href,	method : 'post'	},
		ajaxForm  : null,
		validate  : null,
		spinner   : 'Loading',
		tabs      : { active: 0 },
		tabPrefix : 'el-df-tab-',
		dialog    : {
			title     : 'dialog',
			autoOpen  : false,
			modal     : true,
			resizable : false,
			buttons  : {
				Cancel : function() { self.close(); },
				Ok     : function() { self.form.trigger('submit'); }
			}
		}
	};

	this.opts = $.extend(true, defaults, o, {dialog : { autoOpen : false, close : function() { self.close(); } }});
	if (o && o.dialog && o.dialog.buttons && typeof(o.dialog.buttons) == 'object') {
		this.opts.dialog.buttons = o.dialog.buttons;
	}
	this.ul     = null;
	this.tabs   = {};
	this._table = null;
	this.dialog = $('<div />').addClass(this.opts['class']).dialog(this.opts.dialog);
	this.message = $('<div class="el-dialogform-message rounded-5" />').hide().appendTo(this.dialog);
	this.error   = $('<div class="el-dialogform-error rounded-5" />').hide().appendTo(this.dialog);
	this.spinner = $('<div class="spinner" />').hide().appendTo(this.dialog);
	this.content = $('<div class="el-dialogform-content" />').appendTo(this.dialog)
	this.form   = $('<form />').attr(this.opts.form).appendTo(this.content);

	if (this.opts.submit) {
		this.form.bind('submit', function(e) { self.opts.submit(e, self) })
	}
	if (this.opts.ajaxForm && $.fn.ajaxForm) {
		this.form.ajaxForm(this.opts.ajaxForm);
	}
	if (this.opts.validate) {
		this.form.validate(this.opts.validate);
	}
	
	this.option = function(name, value) {
		return this.dialog.dialog('option', name, value)
	}
	
	this.showError = function(msg, hideContent) {
		this.hideMessage();
		this.hideSpinner();
		this.error.html(msg).show();
		hideContent && this.content.hide();
		return this;
	}
	
	this.hideError= function() {
		this.error.text('').hide();
		this.content.show();
		return this;		
	}
	
	this.showSpinner = function(txt) {
		this.error.hide();
		this.message.hide();
		this.content.hide();
		this.spinner.text(txt||this.opts.spinner).show();
		this.option('buttons', {});
		return this;		
	}
	
	this.hideSpinner = function() {
		this.content.show();
		this.spinner.hide();
		return this;		
	}
	
	this.showMessage = function(txt, hideContent) {
		this.hideError();
		this.hideSpinner();
		this.message.html(txt||'').show();
		hideContent && this.content.hide();
		return this;
	}
	
	this.hideMessage = function() {
		this.message.hide();
		this.content.show();
		return this;		
	}
	
	/**
	 * Create new tab
	 * @param string id    - tab id
	 * @param string title - tab name
	 * @return elDialogForm	
	**/
	this.tab = function(id, title) {
		id = this.opts.tabPrefix+id;
		
		if (!this.ul) {
			this.ul = $('<ul />').prependTo(this.form);
		}
		$('<li />').append($('<a />').attr('href', '#'+id).html(title)).appendTo(this.ul);
		this.tabs[id] = {tab : $('<div />').attr('id', id).addClass('tab').appendTo(this.form), table : null};
		return this;
	}
	
	/**
	 * Create new table
	 * @param string id  tab id, if set - table will create in tab, otherwise - in dialog
	 * @return elDialogForm	
	**/
	this.table = function(id) {
		id = id && id.indexOf(this.opts.tabPrefix) == -1 ? this.opts.tabPrefix+id : id;
		if (id && this.tabs && this.tabs[id]) {
			this.tabs[id].table = $('<table />').appendTo(this.tabs[id].tab);
		} else {
			this._table = $('<table />').appendTo(this.form); 
		}
		return this;
	}
	
	/**
	 * Append html, dom nodes or jQuery objects to dialog or tab
	 * @param array|object|string  data object(s) to append to dialog
	 * @param string               tid  tab id, if adding to tab
	 * @param bool                 t    if true - data will added in table (creating automagicaly)
	 * @return elDialogForm	
	**/
	this.append = function(data, tid, t) {
		tid = tid ? 'el-df-tab-'+tid : '';

		if (!data) {
			return this;
		}
		
		if (tid && this.tabs[tid]) {
			if (t) {
				!this.tabs[tid].table && this.table(tid);
				var tr = $('<tr />').appendTo(this.tabs[tid].table);
				if (!$.isArray(data)) {
					tr.append($('<td />').append(data));
				} else {
					for (var i=0; i < data.length; i++) {
						tr.append($('<td />').append(data[i]));
					};
				}
			} else {
				if (!$.isArray(data)) {
					this.tabs[tid].tab.append(data)
				} else {
					for (var i=0; i < data.length; i++) {
						this.tabs[tid].tab.append(data[i]);
					};
				}
			}
			
		} else {
			if (!t) {
				if (!$.isArray(data)) {
					this.form.append(data);
				} else {
					for (var i=0; i < data.length; i++) {
						this.form.append(data[i]);
					};
				}
			} else {
				if (!this._table) {
					this.table();
				}
				var tr = $('<tr />').appendTo(this._table);
				if (!$.isArray(data)) {
					tr.append($('<td />').append(data));
				} else {
					for (var i=0; i < data.length; i++) {
						tr.append($('<td />').append(data[i]));
					};
				}
			}
		}
		return this;
	}
	
	/**
	 * Append separator (div class="separator") to dialog or tab
	 * @param  string tid  tab id, if adding to tab
	 * @return elDialogForm	
	**/
	this.separator = function(tid) {
		tid = 'el-df-tab-'+tid;
		if (this.tabs && this.tabs[tid]) {
			this.tabs[tid].tab.append($('<div />').addClass('separator'));
			this.tabs[tid].table && this.table(tid);
		} else {
			this.form.append($('<div />').addClass('separator'));
		}
		return this;
	}
	
	/**
	 * Open dialog window
	 * @return elDialogForm	
	**/
	this.open = function() {
		this.ul && this.form.tabs(this.opts.tabs);
		this.form.find(':text').keyup(function(e) {
			if (e.keyCode == 13) {
				self.form.submit();
			}
		});

		this.dialog.attr('unselectable', 'on').dialog('open');
		this.form.find(':text').eq(0).focus();
		return this;
	}
	
	/**
	 * Close dialog window and destroy content
	 * @return void	
	**/
	this.close = function() {
		this.dialog.dialog('destroy').remove();
	}
	
}/**
 * elColorPicker. JQuery plugin
 * Create drop-down colors palette.
 *
 * Usage:
 * $(selector).elColorPicker(opts)
 *
 * set color after init:
 * var c = $(selector).elColorPicker(opts)
 * c.val('#ffff99)
 *
 * Get selected color:
 * var color = c.val();
 *
 * Notice!
 *   Palette created only after first click on element (lazzy loading)
 *
 * Options:
 *   colors - colors array (by default display 256 web safe colors)
 *   color  - current (selected) color
 *   class - css class for display "button" (element on wich plugin was called)
 *   paletteClass - css class for colors palette
 *   palettePosition - string indicate where palette will created:
 *      'inner' - palette will attach to element (acceptable in most cases)
 *      'outer' - palette will attach to document.body. 
 *                Use, when create color picker inside element with overflow == 'hidden', for example in ui.dialog
 *   update - function wich update button view on select color (by default set selected color as background)
 *   change - callback, called when color was selected (by default write color to console.log)
 *   name   - hidden text field in wich selected color value will saved
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 *
 **/
(function($) {

	$.fn.elColorPicker = function(o) {
		var self     = this;
		var opts     = $.extend({}, $.fn.elColorPicker.defaults, o);
		this.hidden  = $('<input type="hidden" />').attr('name', opts.name).val(opts.color||'').appendTo(this);
		this.palette = null;
		this.preview = null;
		this.input   = null;

		function setColor(c) {
			self.val(c);
			opts.change && opts.change(self.val());
			self.palette.slideUp();
		}

		function init() {
			self.palette  = $('<div />').addClass(opts.paletteClass+' rounded-3');
			for (var i=0; i < opts.colors.length; i++) {
				$('<div />')
					.addClass('color')
					.css('background-color', opts.colors[i])
					.attr({title : opts.colors[i], unselectable : 'on'})
					.appendTo(self.palette)
					.mouseenter(function() {
						var v = $(this).attr('title');
						self.input.val(v);
						self.preview.css('background-color', v);
					})
					.click(function(e) {
						e.stopPropagation(); 
						setColor($(this).attr('title'));
					});
			};
			self.input = $('<input type="text" />')
				.addClass('rounded-3')
				.attr('size', 8)
				.click(function(e) {
					e.stopPropagation();
				})
				.keydown(function(e) {
					if (e.ctrlKey || e.metaKey) {
						return true;
					}
					var k = e.keyCode;
					// on esc - close palette
					if (k == 27) {
						return self.mouseleave();
					}
					// allow input only hex color value
					if (k!=8 && k != 13 && k!=46 && k!=37 && k != 39 && (k<48 || k>57) && (k<65 || k > 70)) {
						return false;
					}
					var c = $(this).val();
					if (c.length == 7 || c.length == 0) {
						if (k == 13) {
							e.stopPropagation();
							e.preventDefault();
							setColor(c);
							self.palette.slideUp();
						}
						if (e.keyCode != 8 && e.keyCode != 46 && k!=37 && k != 39) {
							return false;
						}
					}
				})
				.keyup(function(e) {
					var c = $(this).val(); 
					c.length == 7 && /^#[0-9abcdef]{6}$/i.test(c) && self.val(c);
				});
				
			self.preview = $('<div />')
				.addClass('preview rounded-3')
				.click(function(e) {
					e.stopPropagation();
					setColor(self.input.val());
				});
			
			self.palette
				.append($('<div />').addClass('clearfix'))
				.append($('<div />').addClass('panel').append(self.input).append(self.preview));
			
			if (opts.palettePosition == 'outer') {
				self.palette.hide()
					.appendTo(self.parents('body').eq(0))
					.mouseleave(function() {
						$(this).slideUp();
						self.val(self.val());
					})
				self.mouseleave(function(e) {
					if (e.relatedTarget != self.palette.get(0)) {
						self.palette.slideUp();
						self.val(self.val());
					}
				})
			} else {
				self.append(self.palette.hide())
					.mouseleave(function(e) {
						self.palette.slideUp();
						self.val(self.val());
					});
			}
			self.val(self.val());
		}
		
		this.empty().addClass(opts['class']+' rounded-3')
			.css({'position' : 'relative', 'background-color' : opts.color||''})
		.click(function(e) { 
			if (!self.hasClass('disabled')) {
				!self.palette && init();
				if (opts.palettePosition == 'outer' && self.palette.css('display') == 'none') {
					var o = $(this).offset();
					var w = self.palette.width();
					var l = self.parents('body').width() - o.left >= w ? o.left : o.left + $(this).outerWidth() - w;
					self.palette.css({left : l+'px', top : o.top+$(this).height()+1+'px'});
				}
				self.palette.slideToggle();
			}
		});
		
		this.val = function(v) {
			if (!v && v!=='') {
				return this.hidden.val();
			} else {
				this.hidden.val(v);
				if (opts.update) {
					opts.update(this.hidden.val());
				} else {
					this.css('background-color', v);
				}
				
				if (self.palette) {
					self.preview.css('background-color', v);
					self.input.val(v);
				}
			}
			return this;
		}
		
		return this;
	}

	$.fn.elColorPicker.defaults = {
		'class'         : 'el-colorpicker',
		paletteClass    : 'el-palette',
		palettePosition : 'inner',
		name            : 'color',
		color           : '',
		update          : null,
		change          : function(c) { window.console && window.console.log && window.console.log(c) },
		colors          : [
			'#ffffff', '#cccccc', '#999999', '#666666', '#333333', '#000000', 
			'#ffcccc', '#cc9999', '#996666', '#663333', '#330000', 
			'#ff9999', '#cc6666', '#cc3333', '#993333', '#660000', 
			'#ff6666', '#ff3333', '#ff0000', '#cc0000', '#990000',
			'#ff9966', '#ff6633', '#ff3300', '#cc3300', '#993300',
			'#ffcc99', '#cc9966', '#cc6633', '#996633', '#663300',
			'#ff9933', '#ff6600', '#ff9900', '#cc6600', '#cc9933',
			'#ffcc66', '#ffcc33', '#ffcc00', '#cc9900', '#996600',
			'#ffffcc', '#cccc99', '#999966', '#666633', '#333300',
			'#ffff99', '#cccc66', '#cccc33', '#999933', '#666600',
			'#ffff66', '#ffff33', '#ffff00', '#cccc00', '#999900',
			'#ccff66', '#ccff33', '#ccff00', '#99cc00', '#669900',
			'#ccff99', '#99cc66', '#99cc33', '#669933', '#336600',
			'#99ff33', '#99ff00', '#66ff00', '#66cc00', '#66cc33',
			'#99ff66', '#66ff33', '#33ff00', '#33cc00', '#339900',
			'#ccffcc', '#99cc99', '#669966', '#336633', '#003300',
			'#99ff99', '#66cc66', '#33cc33', '#339933', '#006600',
			'#66ff66', '#33ff33', '#00ff00', '#00cc00', '#009900',
			'#66ff99', '#33ff66', '#00ff33', '#00cc33', '#009933',			
			'#99ffcc', '#66cc99', '#33cc66', '#339966', '#006633',						
			'#33ff99', '#00ff66', '#00ff99', '#00cc66', '#33cc99',						
			'#66ffcc', '#33ffcc', '#00ffcc', '#00cc99', '#009966',						
			'#ccffff', '#99cccc', '#669999', '#336666', '#003333',						
			'#99ffff', '#66cccc', '#33cccc', '#339999', '#006666',						
			'#66cccc', '#33ffff', '#00ffff', '#00cccc', '#009999',						
			'#66ccff', '#33ccff', '#00ccff', '#0099cc', '#006699',																		
			'#99ccff', '#6699cc', '#3399cc', '#336699', '#003366',						
			'#3399ff', '#0099ff', '#0066ff', '#066ccc', '#3366cc',																		
			'#6699ff', '#3366ff', '#0033ff', '#0033cc', '#003399',						
			'#ccccff', '#9999cc', '#666699', '#333366', '#000033',																		
			'#9999ff', '#6666cc', '#3333cc', '#333399', '#000066',																		
			'#6666ff', '#3333ff', '#0000ff', '#0000cc', '#009999',																		
			'#9966ff', '#6633ff', '#3300ff', '#3300cc', '#330099',																		
			'#cc99ff', '#9966cc', '#6633cc', '#663399', '#330066',
			'#9933ff', '#6600ff', '#9900ff', '#6600cc', '#9933cc',			
			'#cc66ff', '#cc33ff', '#cc00ff', '#9900cc', '#660099',
			'#ffccff', '#cc99cc', '#996699', '#663366', '#330033',			
			'#ff99ff', '#cc66cc', '#cc33cc', '#993399', '#660066',
			'#ff66ff', '#ff33ff', '#ff00ff', '#cc00cc', '#990099',			
			'#ff66cc', '#ff33cc', '#ff00cc', '#cc0099', '#990066',
			'#ff99cc', '#cc6699', '#cc3399', '#993366', '#660033',			
			'#ff3399', '#ff0099', '#ff0066', '#cc0066', '#cc3366',
			'#ff6699', '#ff3366', '#ff0033', '#cc0033', '#990033'		
			]
	};

})(jQuery);
/**
 * jQuery plugin. Create group of text input, elSelect and elColorPicker. 
 * Allow input border-width, border-style and border-color. Used in elRTE
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/
(function($) {
	
	$.fn.elBorderSelect = function(o) {
		
		var $self = this;
		var self  = this.eq(0);
		var opts  = $.extend({}, $.fn.elBorderSelect.defaults, o);
		var width = $('<input type="text" />')
			.attr({'name' : opts.name+'[width]', size : 3}).css('text-align', 'right')
			.change(function() { $self.change(); });
		
		var color = $('<div />').css('position', 'relative')
			.elColorPicker({
				'class'         : 'el-colorpicker ui-icon ui-icon-pencil',
				name            : opts.name+'[color]', 
				palettePosition : 'outer',
				change          : function() { $self.change(); }
			});
		
		
		var style = $('<div />').elSelect({
			tpl       : '<div style="border-bottom:4px %val #000;width:100%;margin:7px 0"> </div>',
			tpls      : { '' : '%label'},
			maxHeight : opts.styleHeight || null,
			select    : function() { $self.change(); },
			src       : {
				''       : 'none',
				solid    : 'solid',
				dashed   : 'dashed',
				dotted   : 'dotted',
				'double' : 'double',
				groove   : 'groove',
				ridge    : 'ridge',
				inset    : 'inset',
				outset   : 'outset'
			}
		});
		
		self.empty()
			.addClass(opts['class'])
			.attr('name', opts.name||'')
			.append(
				$('<table />').attr('cellspacing', 0).append(
					$('<tr />')
						.append($('<td />').append(width).append(' px'))
						.append($('<td />').append(style))
						.append($('<td />').append(color))
				)
			);
		
		function rgb2hex(str) {
		    function hex(x)  {
		    	hexDigits = ["0", "1", "2", "3", "4", "5", "6", "7", "8","9", "a", "b", "c", "d", "e", "f"];
		        return !x  ? "00" : hexDigits[(x - x % 16) / 16] + hexDigits[x% 16];
		    }
			var rgb = str.match(/\(([0-9]{1,3}),\s*([0-9]{1,3}),\s*([0-9]{1,3})\)/); 
			return rgb ? "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]) : '';
		}
		
		function toPixels(num) {
			var m = num.match(/([0-9]+\.?[0-9]*)\s*(px|pt|em|%)/);
			if (m) {
				num  = m[1];
				unit = m[2];
			} 
			if (num[0] == '.') {
				num = '0'+num;
			}
			num = parseFloat(num);

			if (isNaN(num)) {
				return '';
			}
			var base = parseInt($(document.body).css('font-size')) || 16;
			switch (unit) {
				case 'em': return parseInt(num*base);
				case 'pt': return parseInt(num*base/12);
				case '%' : return parseInt(num*base/100);
			}
			return num;
		}
		
		this.change = function() {
			opts.change && opts.change(this.val());
		}
		
		this.val = function(v) {
			if (!v && v !== '') {
				var w = parseInt(width.val());
				return {width : !isNaN(w) ? w+'px' : '', style : style.val(), color : color.val()};
			} else {
				var m, w, s, c, b = '';
				if (v.nodeName || v.css) {
					if (!v.css) {
						v = $(v);					
					}
					var b = v.css('border')
					if ((b = v.css('border'))) {
						w = s = c = b;
					} else {
						w = v.css('border-width');
						s = v.css('border-style');
						c = v.css('border-color');
					}

				} else {
					w = v.width||'';
					s = v.style||'';
					c = v.color||'';
				}

				width.val(toPixels(w));
				var m = s.match(/(solid|dashed|dotted|double|groove|ridge|inset|outset)/i);
				style.val(m ? m[1] : '');
				color.val(rgb2hex(c));
				return this;
			}
		}
		
		this.val(opts.value);
		return this;
	}
	
	$.fn.elBorderSelect.defaults = {
		name      : 'el-borderselect',
		'class'   : 'el-borderselect',
		value     : {},
		change    : null
	}
	
})(jQuery);
/**
 * jQuery plugin. Context menu
 *
 * Usage:
 * var opts = {
 *	'.dir-r' : [
 *		{label : '<span class="icon icon-open"></span> Open',     action : function(o) { window.console.log(o); } },
 *		{},
 *		{label : '<span class="icon icon-rename"></span> Rename', action : function(o) { window.console.log(o); } },
 *		{},
 *		{label : '<span class="icon icon-remove"></span> Remove', action : function(o) { window.console.log(o); } }
 *	]
 * }
 * $('#selector').elcontextmenu(opts);
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/
(function($) {
	
	var selectors = {};
	var menu = document.createElement('div');
	/**
	 * Bind function showmenu on right click 
	 *
	 * @param   Object - context menu options. Object.
	 * @context DOMElement - context for menu. if not set, document.body used
	 * @callback Function  - callback for menu click
	 **/
	$.elcontextmenu = function(options, context, callback) {
		context = context||document.body;
		$(menu).hide().addClass('el-contextmenu rounded-5').appendTo(document.body);
		for (name in options) {
			selectors[name] = options[name];
			$(name, context).bind(window.opera?'click':'contextmenu', showmenu);
		}
		menu.callback = callback;
	};
	
	function showmenu(event) {
		event.stopPropagation();
		reset();
		if (window.opera && !event.ctrlKey) { 
			return;  
		} else {
	      $(document.body).mousedown(function(event){ reset(); });
	    }
		for (name in selectors) {
			if ($.inArray(event.currentTarget, $(name)) > -1) {
				if (menu.callback) {
					menu.callback(event);
				}
				variants = selectors[name]
				$(variants).each( function() {
					if (!this.label) {
						$('<div />').addClass('delim').appendTo(menu);
					} else {
						var action = this.action
						$('<div></div>').html(this.label).mousedown(function(clickEvent) { 
							clickEvent.stopPropagation();
							reset();
							if (typeof(action) == 'function') {
								action(event.currentTarget);
							}
						})
						.hover( 
							function() { $(this).addClass('el-contextmenu-hover'); },
							function() { $(this).removeClass('el-contextmenu-hover'); }
						)
						.appendTo(menu);
					}
				})
			}
		}
		
		var size = {
	      'height' : $(window).height(),
	      'width'  : $(window).width(),
	      'sT'     : $(window).scrollTop(),
	      'cW'     : $(menu).width(),
	      'cH'     : $(menu).height()
	    };
		$(menu).css({
				'left' : ((event.clientX + size.cW) > size.width ? ( event.clientX - size.cW) : event.clientX),
				'top'  : ((event.clientY + size.cH) > size.height && event.clientY > size.cH ? (event.clientY + size.sT - size.cH) : event.clientY + size.sT)
			}).show();
	    return false;
	}
	
	function reset(event){ $(menu).hide().empty(); }
	
})(jQuery);
/**
 * jQuery plugin. Create directory tree, like Finder or Explorer directory tree
 * Used in elFinder.
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/
(function($) {

	$.fn.eldirtree = function(o) {
		if (!options) {
			var options = o && o.constructor == Object
				? $.extend({}, $.fn.eldirtree.defaults, o)
				: $.fn.eldirtree.defaults;
		}

		return this.each(function() {
			var self = this;
			
			if (!this.loaded) {
				this.loaded = true;
				var root = $(this).addClass(options.cssClass)
					.find('li').prepend($('<div />')).filter(':has(ul)').children('div').addClass('el-dir-collapsed').click(function(e) {
						if ($(this).hasClass('el-dir-expanded')) {
							$(this).removeClass('el-dir-expanded').next('a').removeClass('el-dir-expanded').parent('li').children('ul').hide();
						} else {
							$(this).addClass('el-dir-expanded').next('a').addClass('el-dir-expanded').parent('li').children('ul').show();
						}
					}).end().end().end()
					.children('li').find('a').addClass('el-dir-collapsed rounded-3').end();
					
				if (root.length == 1) {
					root.children('a, div').addClass('el-dir-expanded').end().find('ul li ul').hide();
				} else {
					root.find('ul').hide();
				}

				root.find('a').bind('click cd', function(e) {
					e.stopPropagation();
					e.preventDefault();
					root.find('a').removeClass('selected');
					$(this).addClass('selected').parent().parents('li').filter(':has(ul)').children('a, div').addClass('el-dir-expanded').end().children('ul').show();
					e.type == 'click' && options.callback($(this));
				})
				root.eq(0).children('a').addClass('selected');
			}
		});
	}

	$.fn.eldirtree.defaults = {
		cssClass : 'el-dir-tree',
		callback : function() {}	
	};

})(jQuery);

/**
 * jQuery plugin. Create group of text input fields and selects for setting padding/margin. Used in elRTE
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/
(function($) {
	
	$.fn.elPaddingInput = function(o) {
		var self = this;
		var opts = $.extend({}, $.fn.elPaddingInput.defaults, {name : this.attr('name')}, o);
		this.regexps = {
			main   : new RegExp(opts.type == 'padding' ? 'padding\s*:\s*([^;"]+)'        : 'margin\s*:\s*([^;"]+)',       'im'),
			left   : new RegExp(opts.type == 'padding' ? 'padding-left\s*:\s*([^;"]+)'   : 'margin-left\s*:\s*([^;"]+)',  'im'),
			top    : new RegExp(opts.type == 'padding' ? 'padding-top\s*:\s*([^;"]+)'    : 'margin-top\s*:\s*([^;"]+)',    'im'),
			right  : new RegExp(opts.type == 'padding' ? 'padding-right\s*:\s*([^;"]+)'  : 'margin-right\s*:\s*([^;"]+)',  'im'),
			bottom : new RegExp(opts.type == 'padding' ? 'padding-bottom\s*:\s*([^;"]+)' : 'margin-bottom\s*:\s*([^;"]+)', 'im')
		};
			
		$.each(['left', 'top', 'right', 'bottom'], function() {
			self[this] = $('<input type="text" />')
				.attr('size', 3)
				.css('text-align', 'right')
				.bind('change', function() { $(this).val(parseNum($(this).val())); change(); })
				.attr('name', opts.name+'['+this+']');
		});
		$.each(['uleft', 'utop', 'uright', 'ubottom'], function() {
			self[this] = $('<select />')
				.append('<option value="px">px</option>')
				.append('<option value="em">em</option>')
				.append('<option value="pt">pt</option>')
				.bind('change', function() { change(); })
				.attr('name', opts.name+'['+this+']');
			if (opts.percents) {
				self[this].append('<option value="%">%</option>');
			}
		});
		
		this.empty().addClass(opts['class'])
			.append(this.left).append(this.uleft).append(' x ')
			.append(this.top).append(this.utop).append(' x ')
			.append(this.right).append(this.uright).append(' x ')
			.append(this.bottom).append(this.ubottom);
			
		this.val = function(v) {
			if (!v && v!=='') {
				var l = parseNum(this.left.val());
				var t = parseNum(this.top.val());
				var r = parseNum(this.right.val());
				var b = parseNum(this.bottom.val());
				var ret = {
					left   : l=='auto' || l==0 ? l : (l!=='' ? l+this.uleft.val()   : ''), 
					top    : t=='auto' || t==0 ? t : (t!=='' ? t+this.utop.val()    : ''),
					right  : r=='auto' || r==0 ? r : (r!=='' ? r+this.uright.val()  : ''),
					bottom : b=='auto' || b==0 ? b : (b!=='' ? b+this.ubottom.val() : ''),
					css    : ''
				};
				if (ret.left!=='' && ret.right!=='' && ret.top!=='' && ret.bottom!=='') {
					if (ret.left == ret.right && ret.top == ret.bottom) {
						ret.css = ret.top+' '+ret.left;
					} else{
						ret.css = ret.top+' '+ret.right+' '+ret.bottom+' '+ret.left;
					}
				}
				
				return ret;
			} else {
				
				if (v.nodeName || v.css) {
					if (!v.css) {
						v = $(v);
					}
					var val   = {left : '', top : '', right: '', bottom : ''};
					var style = (v.attr('style')||'').toLowerCase();

					if (style) {
						style   = $.trim(style);
						var m = style.match(this.regexps.main);
						if (m) {
							var tmp    = $.trim(m[1]).replace(/\s+/g, ' ').split(' ', 4);
							val.top    = tmp[0];
							val.right  = tmp[1] && tmp[1]!=='' ? tmp[1] : val.top;
							val.bottom = tmp[2] && tmp[2]!=='' ? tmp[2] : val.top;
							val.left   = tmp[3] && tmp[3]!=='' ? tmp[3] : val.right;
						} else {
							$.each(['left', 'top', 'right', 'bottom'], function() {
								var name = this.toString();
								m = style.match(self.regexps[name]);
								if (m) {
									val[name] = m[1];
								}
							});
						}
					}
					var v = val;
				} 

				$.each(['left', 'top', 'right', 'bottom'], function() {
					var name = this.toString();
					if (typeof(v[name]) != 'undefined' && v[name] !== null) {
						v[name] = v[name].toString();
						var _v = parseNum(v[name]);
						self[name].val(_v);
						var m = v[name].match(/(px|em|pt|%)/i);
						self['u'+name].val(m ? m[1] : 'px');
					}
				});
				return this;
			}
		}
			
		function parseNum(num) {
			num = $.trim(num.toString());
			if (num[0] == '.') { 
				num = '0'+num;
			}
			n = parseFloat(num);
			return !isNaN(n) ? n : (num == 'auto' ? num : '');
		}
			
		function change() {
			opts.change && opts.change(self);
		}
		
		this.val(opts.value);
		
		return this;
	}
	
	$.fn.elPaddingInput.defaults = {
		name     : 'el-paddinginput',
		'class'  : 'el-paddinginput',
		type     : 'padding',
		value    : {},
		percents : true,
		change   : null
	}
	
})(jQuery);
/**
 * elSelect JQuery plugin
 * Replacement for select input
 * Allow to put any html and css decoration in drop-down list
 *
 * Usage:
 *   $(selector).elSelect(opts)
 *
 * set value after init:
 *   var c = $(selector).elSelect(opts)
 *   c.val('some value')
 *
 * Get selected value:
 *   var val = c.val();
 *
 * Notice!
 *   1. When called on multiply elements, elSelect create drop-down list only for fist element
 *   2. Elements list created only after first click on element (lazzy loading)
 *
 * Options:
 *   src       - object with pairs value:label to create drop-down list 
 *   value     - current (selected) value
 *   class     - css class for display "button" (element on wich plugin was called)
 *   listClass - css class for drop down elements list
 *   select    - callback, called when value was selected (by default write value to console.log)
 *   name      - hidden text field in wich selected value will saved
 *   maxHeight - elements list max height (if height greater - scroll will appear)
 *   tpl       - template for element in list (contains 2 vars: %var - for src key, %label - for src[val] )
 *   labelTpl  - template for label (current selected element) (contains 2 placeholders: %var - for src key, %label - for src[val] )
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/
(function($) {
	
	$.fn.elSelect = function(o) {
		var $self    = this;
		var self     = this.eq(0);
		var opts     = $.extend({}, $.fn.elSelect.defaults, o);
		var hidden   = $('<input type="hidden" />').attr('name', opts.name);
		var label    = $('<label />').attr({unselectable : 'on'}).addClass('rounded-left-3');
		var list     = null;
		var ieWidth  = null;

		if (self.get(0).nodeName == 'SELECT') {
			opts.src = {};
			self.children('option').each(function() {
				opts.src[$(this).val()] = $(this).text();
			});
			opts.value = self.val();
			opts.name  = self.attr('name');
			self.replaceWith((self = $('<div />')));
		}
		
		if (!opts.value || !opts.src[opts.val]) {
			opts.value = null;
			var i = 0;
			for (var v in opts.src) {
				if (i++ == 0) {
					opts.value = v;
				}
			}
		}

		this.val = function(v) {
			if (!v && v!=='') {
				return hidden.val();
			} else {
				if (opts.src[v]) {
					hidden.val(v);
					updateLabel(v);
					if (list) {
						list.children().each(function() {
							if ($(this).attr('name') == v) {
								$(this).addClass('active');
							} else {
								$(this).removeClass('active');
							}
						});
					}
				}
				return this;
			}
		}
	
		// update label content
		function updateLabel(v) {
			var tpl = opts.labelTpl || opts.tpls[v] || opts.tpl;
			label.html(tpl.replace(/%val/g, v).replace(/%label/, opts.src[v])).children().attr({unselectable : 'on'});
		}
		
		// init "select"
		self.empty()
			.addClass(opts['class']+' rounded-3')
			.attr({unselectable : 'on'})
			.append(hidden)
			.append(label)
			.hover(
				function() { $(this).addClass('hover') },
				function() { $(this).removeClass('hover') }
			)
			.click(function(e) {
				!list && init();
				list.slideToggle();
				// stupid ie inherit width from parent
				if ($.browser.msie && !ieWidth) { 
					list.children().each(function() {
						ieWidth = Math.max(ieWidth, $(this).width());
					});
					if (ieWidth > list.width()) {
						list.width(ieWidth+40);
					}
				}
			});
			
		this.val(opts.value);
	
		// create drop-down list
		function init() {
			// not ul because of ie is stupid with mouseleave in it :(
			list = $('<div />')
				.addClass(opts.listClass+' rounded-3')
				.hide()
				.appendTo(self.mouseleave(function(e) { list.slideUp(); }));

			for (var v in opts.src) {
				var tpl = opts.tpls[v] || opts.tpl; 
				$('<div />')
					.attr('name', v)
					.append( $(tpl.replace(/%val/g, v).replace(/%label/g, opts.src[v])).attr({unselectable : 'on'}) )
					.appendTo(list)
					.hover(
						function() { $(this).addClass('hover') },
						function() { $(this).removeClass('hover') }
					)
					.click(function(e) {
						e.stopPropagation();
						e.preventDefault();
						
						var v = $(this).attr('name');
						$self.val(v);
						opts.select(v);
						list.slideUp();
					});
			};
			
			var w = self.outerWidth();
			if (list.width() < w) {
				list.width(w);
			}
			
			var h = list.height();
			if (opts.maxHeight>0 && h>opts.maxHeight) {
				list.height(opts.maxHeight);
			}
			
			$self.val(hidden.val());
		}
		
		return this;
	}
	
	$.fn.elSelect.defaults = {
		name      : 'el-select',
		'class'   : 'el-select',
		listClass : 'list',
		labelTpl  : null,
		tpl       : '<%val>%label</%val>',
		tpls      : {},
		value     : null,
		src       : {},
		select    : function(v) {  window.console &&  window.console.log && window.console.log('selected: '+v); },
		maxHeight : 310
	}
	
})(jQuery);
/*
 * elRTE - WSWING editor for web
 *
 * Usage:
 * var opts = {
 *	.... // see elRTE.options.js
 * }
 * var editor = new elRTE($('#my-id').get(0), opts)
 * or
 * $('#my-id').elrte(opts)
 *
 * $('#my-id) may be textarea or any DOM Element with text
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 */
(function($) {
	
elRTE = function(target, opts) {
	var self     = this;
	this.version = '1.0 RC1';
	this.options = $.extend(true, {}, this.options, opts);
	this.browser = $.browser;
	
	this.editor    = $('<div />').addClass(this.options.cssClass);
	this.toolbar   = $('<div />').addClass('toolbar').appendTo(this.editor);
	this.iframe    = document.createElement('iframe');
	this.workzone  = $('<div />').addClass('workzone').appendTo(this.editor).append(this.iframe);
	this.statusbar = $('<div />').addClass('statusbar').appendTo(this.editor);
	this.tabsbar   = $('<div />').addClass('tabsbar').appendTo(this.editor);
	this.source    = $('<textarea />').appendTo(this.workzone).hide();
	
	this.target  = null;
	this.doc     = null;
	this.window  = null;
	
	this.utils     = new this.utils(this);
	this.dom       = new this.dom(this);
	this._i18n     = new eli18n({textdomain : 'rte', messages : { rte : this.i18Messages[this.options.lang] || {}} });	

	if (!target || !target.nodeName) {
		alert('elRTE: argument "target" is not DOM Element');
		return;
	}
	
	this.init = function() {
		this.options.height>0 && this.workzone.height(this.options.height);
		var src = this.filter(target.nodeName == 'TEXTAREA' ? $(target).val() : $(target).html(), true);
		this.source.val(src);
		this.source.attr('name', $(target).attr('name')||$(target).attr('id'));
		if (this.options.allowSource) {
			this.tabsbar.append($('<div />').text(self.i18n('Editor')).addClass('tab editor rounded-bottom-7 active'))
						.append($('<div />').text(self.i18n('Source')).addClass('tab source rounded-bottom-7'))
						.append($('<div />').addClass('clearfix'));
		}
		this.target = $(target).replaceWith(this.editor);
		this.window = this.iframe.contentWindow;
		this.doc    = this.iframe.contentWindow.document;
		
		html = '<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
		if (self.options.cssfiles.length) {
			$.each(self.options.cssfiles, function() {
				html += '<link rel="stylesheet" type="text/css" href="'+this+'" />';
			});
		}
		html = self.options.doctype+html+'</head><body>'+src+'</body></html>';
		this.doc.open();
		this.doc.write(html);
		this.doc.close();
		if(!this.doc.body.firstChild) {
			this.doc.body.appendChild(this.doc.createElement('br'));
		}
		if (this.browser.msie) {
			//this.source.attr('rows', parseInt(this.options.height/17));
			this.doc.body.contentEditable = true;
		} else {
			try { this.doc.designMode = "on"; } 
			catch(e) { }
			this.doc.execCommand('styleWithCSS', false, this.options.styleWithCSS);
		}
		 
		this.window.focus();
		this.selection = new this.selection(this);
		this.ui = new this.ui(this);
		this.editor.parents('form').eq(0).submit(function(e) {
			if (self.source.css('display') == 'none') {
				self.updateSource();
			}
			self.toolbar.find(':hidden').remove();
		});
		
		$(this.doc)
			.keydown(function(e) {
				if (self.browser.safari && e.keyCode == 13) {
			
					if (e.shiftKey || !self.dom.parent(self.selection.getNode(), /^(P|LI)$/)) {
						self.selection.insertNode(self.doc.createElement('br'))
						return false;
					}
				}
			})
			.bind('keyup mouseup', function(e) {
				if (e.type == 'mouseup' || e.ctrlKey || e.metaKey || (e.keyCode >= 8 && e.keyCode <= 13) || (e.keyCode>=32 && e.keyCode<= 40) || e.keyCode == 46 || (e.keyCode >=96 && e.keyCode <= 111)) {
					self.ui.update();
				}
			});
	}
	
	this.init();
	
	

}

/**
 * Return message translated to selected language
 *
 * @param  string  msg  message text in english
 * @return string
 **/
elRTE.prototype.i18n = function(msg) {
	return this._i18n.translate(msg);
}



/**
 * Display editor
 *
 * @return void
 **/
elRTE.prototype.open = function() {
	this.editor.show();
	this.target.hide();
}

/**
 * Hide editor and display elements on wich editor was created
 *
 * @return void
 **/
elRTE.prototype.close = function() {
	this.editor.hide();
	this.target.show();
}

elRTE.prototype.updateEditor = function() {
	$(this.doc.body).html( this.filter(this.source.val(), true) );
	this.window.focus();
	this.ui.update(true);
}

elRTE.prototype.updateSource = function() {
	this.source.val(this.filter($(this.doc.body).html()));
	
}

/**
 * Return edited text
 *
 * @return String
 **/
elRTE.prototype.val = function(val) {
	if (val) {
		$(this.doc.body).html( this.filter(val, true) );
	} else {
		this.updateSource();
		return this.source.val();
	}
}

/**
 * Submit form
 *
 * @return void
 **/
elRTE.prototype.save = function() {
	this.editor.parents('form').submit();
}


elRTE.prototype.filter = function(v, input) {
	var html = '';
	var node = $('<span />');
	if (!v.nodeType) {
		html = $.trim(v);
	} else {
		html = $.trim(v.nodeType == 1 ? $(v).html() : v.nodeValue);
	}
	var sw = this.options.stripWhiteSpace;
	$.each(this.filters.html, function() {
		html = this(html, sw);
	});
	
	node.html(html);

	if (input) {
		node.find('a').each(function() {
			if ($(this).attr('name')) {
				$(this).addClass('el-rte-anchor');
			}
		});
		
	} else {
		node.find('a.el-rte-anchor').each(function() {
			if ($.trim($(this).attr('class')) == 'el-rte-anchor') {
				$(this).removeAttr('class');
			} else {
				$(this).removeClass('el-rte-anchor');
			}
		});
	}

	$.each(this.filters.dom, function() {
		node = this(node);
	});
	return node.html();
}



elRTE.prototype.filters = {
	dom  : [
		function(n) { 
			n.find('[align]').not('tbody,tr').each(function() {
				var a = ($(this).attr('align')||'').toLowerCase();
				if ((this.nodeName != 'TD' && this.nodeName != 'TH') || a != 'left') {
					$(this).css('text-align', a).removeAttr('align');
				}
			})
			.end().end().find('[border],[bordercolor]').each(function() {
				var w = parseInt($(this).attr('border')) || 1,
					c = $(this).attr('bordercolor') || '#000';
				$(this).css('border', w+'px solid '+c).removeAttr('border').removeAttr('bordercolor');
			})
			.end().find('[bgcolor]').each(function() {
				$(this).css('background-color', $(this).attr('bgcolor')).removeAttr('bgcolor');
			}).end().find('[background]').each(function() {
				$(this).css('background', 'url('+$(this).attr('background')+')' ).removeAttr('background');
			})
			.end().find('img[hspace],[vspace]').each(function() {
				var v = parseInt($(this).attr('vspace'))||0,
					h = parseInt($(this).attr('hspace'))||0;
				if (v>0 || h>0) {
					$(this).css('margin', (v>0?v:0)+'px '+(h>0?h:0)+'px');
				}
				$(this).removeAttr('hspace').removeAttr('vspace');
			})
			.end().find('[clear]').each(function() {
				var c = ($(this).attr('clear')||"").toLowerCase();
				$(this).css('clear', c == 'all' ? 'both' : c);
			});
			
			if ($.browser.safari) {
				n.find('.Apple-style-span').removeClass('Apple-style-span');
			}
			return n;
		}
	],
	html : [
		function(html, stripWhiteSpace) { 
			var fsize = {
				1 : 'xx-small',
				2 : 'x-small',
				3 : 'small',
				4 : 'medium',
				5 : 'large',
				6 : 'x-large',
				7 : 'xx-large'
			}
			
			html = html.replace(/<font([^>]*)/i, function(str, attr) {
				var css = '', m = attr.match(/size=('|")(\d)/i);
				if (m && m[2] && fsize[m[2]]) {
					css = 'font-size: '+fsize[m[2]]+'; ';
				}
				m = attr.match(/face=('|")([a-z0-9\s,]+)/i);
				if (m && m[2]) {
					css += 'font-family: '+m[2];
				}
				return '<span'+(css ? ' style="'+css+'"' : '');
			})
			.replace(/<\/font/i, '</span')
			.replace(/<b(\s[^>]*)?>/i, '<strong$1>')
			.replace(/<\/b\s*>/i, '</strong>')
			.replace(/<i(\s[^>]*)?>/i, '<em$1>')
			.replace(/<\/i\s*>/i, '</em>')
			.replace(/((class|style)="")/i, '');
			
			//.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '')
			if (stripWhiteSpace) {
				html = html.replace(/\r?\n(\s)*/mg, "\n");
			}
			return html 
		}
	]
}

elRTE.prototype.log = function(msg) {
	if (window.console && window.console.log) {
		window.console.log(msg);
	}
        
}

elRTE.prototype.i18Messages = {};

$.fn.elrte = function(o) { 
	return this.each(function() {
		var rte = new elRTE(this, o);
	});
}

})(jQuery);
/*
 * DOM utilites for elRTE 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 */
elRTE.prototype.dom = function(rte) {
	this.rte = rte;
	var self = this;
	this.regExp = {
		textNodes         : /^(A|ABBR|ACRONYM|ADDRESS|B|BDO|BIG|BLOCKQUOTE|CAPTION|CENTER|CITE|CODE|DD|DEL|DFN|DIV|DT|EM|FIELDSET|FONT|H[1-6]|I|INS|KBD|LABEL|LEGEND|LI|MARQUEE|NOBR|NOEMBED|P|PRE|Q|SAMP|SMALL|SPAN|STRIKE|STRONG|SUB|SUP|TD|TH|TT|VAR)$/,
		textContainsNodes : /^(A|ABBR|ACRONYM|ADDRESS|B|BDO|BIG|BLOCKQUOTE|CAPTION|CENTER|CITE|CODE|DD|DEL|DFN|DIV|DL|DT|EM|FIELDSET|FONT|H[1-6]|I|INS|KBD|LABEL|LEGEND|LI|MARQUEE|NOBR|NOEMBED|OL|P|PRE|Q|SAMP|SMALL|SPAN|STRIKE|STRONG|SUB|SUP|TABLE|THEAD|TBODY|TFOOT|TD|TH|TR|TT|UL|VAR)$/,
		block             : /^(APPLET|BLOCKQUOTE|BR|CAPTION|CENTER|COL|COLGROUP|DD|DIV|DL|DT|H[1-6]|EMBED|FIELDSET|LI|MARQUEE|NOBR|OBJECT|OL|P|PRE|TABLE|THEAD|TBODY|TFOOT|TD|TH|TR|UL)$/,
		selectionBlock    : /^(APPLET|BLOCKQUOTE|BR|CAPTION|CENTER|COL|COLGROUP|DD|DIV|DL|DT|H[1-6]|EMBED|FIELDSET|LI|MARQUEE|NOBR|OBJECT|OL|P|PRE|TD|TH|TR|UL)$/,		
		header            : /^H[1-6]$/,
		formElement       : /^(FORM|INPUT|HIDDEN|TEXTAREA|SELECT|BUTTON)$/
	};
	
	/********************************************************/
	/*                      Ð£Ñ‚Ð¸Ð»Ð¸Ñ‚Ñ‹                         */
	/********************************************************/	
	
	/**
	 * Ð’Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ body Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€ÑƒÐµÐ¼Ð¾Ð³Ð¾ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°
	 *
	 * @return Element
	 **/
	this.root = function() {
		return this.rte.body;
	}

	this.create = function(t) {
		return this.rte.doc.createElement(t);
	}

	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð¸Ð½Ð´ÐµÐºÑ ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ð° Ð²Ð½ÑƒÑ‚Ñ€Ð¸ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ
	 *
	 * @param  Element n  Ð½Ð¾Ð´Ð°
	 * @return integer
	 **/
	this.indexOf = function(n) {
		var ndx = 0;
		n = $(n);
		while ((n = n.prev()) && n.length) {
			ndx++;
		}
		return ndx;
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ðµ Ð°Ñ‚Ñ‚Ñ€Ð¸Ð±ÑƒÑ‚Ð° Ð² Ð½Ð¸Ð¶Ð½ÐµÐ¼ Ñ€ÐµÐ³Ð¸ÑÑ‚Ñ€Ðµ (Ð¾Ñ… ÑƒÐ¶ ÑÑ‚Ð¾Ñ‚ IE)
	 *
	 * @param  Element n  Ð½Ð¾Ð´Ð°
	 * @param  String  attr Ð¸Ð¼Ñ Ð°Ñ‚Ñ‚Ñ€Ð¸Ð±ÑƒÑ‚Ð°
	 * @return string
	 **/
	this.attr = function(n, attr) {
		var v = '';
		if (n.nodeType == 1) {
			v = $(n).attr(attr);
			if (v && attr != 'src' && attr != 'href') {
				v = v.toString().toLowerCase();
			}
		} 
		return v||'';
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð±Ð»Ð¸Ð¶Ð°Ð¹ÑˆÐ¸Ð¹ Ð¾Ð±Ñ‰Ð¸Ð¹ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€ Ð´Ð»Ñ 2-Ñ… ÑÐ»-Ñ‚Ð¾Ð²
	 *
	 * @param  Element n  Ð½Ð¾Ð´Ð°1
	 * @param  Element n  Ð½Ð¾Ð´Ð°2
	 * @return Element
	 **/
	this.findCommonAncestor = function(n1, n2) {
		if (!n1 || !n2) {
			return this.rte.log('dom.findCommonAncestor invalid arguments');
		}
		if (n1 == n2) {
			return n1;
		} else if (n1.nodeName == 'BODY' || n2.nodeName == 'BODY') {
			return this.rte.doc.body;
		}
		var p1 = $(n1).parents(), p2 = $(n2).parents(), l  = p2.length-1, c  = p2[l];
		for (var i = p1.length - 1; i >= 0; i--, l--){
			if (p1[i] == p2[l]) {
				c = p1[i];
			} else {
				break;
			}
		};
		return c;
	}
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ TRUE, ÐµÑÐ»Ð¸ Ð½Ð¾Ð´Ð° Ð¿ÑƒÑÑ‚Ð°Ñ
	 * Ð¿ÑƒÑÑ‚Ð¾Ð¹ ÑÑ‡Ð¸Ñ‚Ð°ÐµÐ¼ Ð½Ð¾Ð´Ñ‹:
	 *  - Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ðµ ÑÐ»-Ñ‚Ñ‹, ÑÐ¾Ð´ÐµÑ€Ð¶Ð°Ñ‰Ð¸Ðµ Ð¿ÑƒÑÑ‚ÑƒÑŽ ÑÑ‚Ñ€Ð¾ÐºÑƒ Ð¸Ð»Ð¸ Ñ‚ÐµÐ³ br
	 *  - Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ðµ Ð½Ð¾Ð´Ñ‹ Ñ Ð¿ÑƒÑÑ‚Ð¾Ð¹ ÑÑ‚Ñ€Ð¾ÐºÐ¾Ð¹
	 *
	 * @param  DOMElement n  Ð½Ð¾Ð´Ð°
	 * @return bool
	 **/
	this.isEmpty = function(n) {
		if (n.nodeType == 1) {
			return this.regExp.textNodes.test(n.nodeName) ? $.trim($(n).text()).length == 0 : false;
		} else if (n.nodeType == 3) {
			return /^(TABLE|THEAD|TFOOT|TBODY|TR|UL|OL|DL)$/.test(n.parentNode.nodeName)
				|| n.nodeValue == ''
				|| ($.trim(n.nodeValue).length== 0 && !(n.nextSibling && n.previousSibling && n.nextSibling.nodeType==1 && n.previousSibling.nodeType==1 && !this.regExp.block.test(n.nextSibling.nodeName) && !this.regExp.block.test(n.previousSibling.nodeName) ));
		}
		return true;
	}

	/********************************************************/
	/*                  ÐŸÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸Ðµ Ð¿Ð¾ DOM                  */
	/********************************************************/

	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ ÑÐ»ÐµÐ´ÑƒÑŽÑ‰ÑƒÑŽ ÑÐ¾ÑÐµÐ´Ð½ÑŽÑŽ Ð½Ð¾Ð´Ñƒ (Ð½Ðµ Ð²ÐºÐ»ÑŽÑ‡Ð°ÑŽÑ‚ÑÑ Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ðµ Ð½Ð¾Ð´Ñ‹ Ð½Ðµ ÑÐ¾Ð·Ð´Ð°ÑŽÑ‰Ð¸Ðµ Ð·Ð½Ð°Ñ‡Ð¸Ð¼Ñ‹Ðµ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ñ‹ Ð¼ÐµÐ¶Ð´Ñƒ Ð¸Ð½Ð»Ð°Ð¹Ð½ ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ð°Ð¼Ð¸)
	 *
	 * @param  DOMElement n  Ð½Ð¾Ð´Ð°
	 * @return DOMElement
	 **/
	this.next = function(n) {
		while (n.nextSibling && (n = n.nextSibling)) {
			if (n.nodeType == 1 || (n.nodeType == 3 && !this.isEmpty(n))) {
				return n;
			}
		}
		return null;
	}

	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð¿Ñ€ÐµÐ´Ñ‹Ð´ÑƒÑŽÑ‰ÑƒÑŽ ÑÐ¾ÑÐµÐ´Ð½ÑŽÑŽ Ð½Ð¾Ð´Ñƒ (Ð½Ðµ Ð²ÐºÐ»ÑŽÑ‡Ð°ÑŽÑ‚ÑÑ Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ðµ Ð½Ð¾Ð´Ñ‹ Ð½Ðµ ÑÐ¾Ð·Ð´Ð°ÑŽÑ‰Ð¸Ðµ Ð·Ð½Ð°Ñ‡Ð¸Ð¼Ñ‹Ðµ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ñ‹ Ð¼ÐµÐ¶Ð´Ñƒ Ð¸Ð½Ð»Ð°Ð¹Ð½ ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ð°Ð¼Ð¸)
	 *
	 * @param  DOMElement n  Ð½Ð¾Ð´Ð°
	 * @return DOMElement
	 **/
	this.prev = function(n) {
		while (n.previousSibling && (n = n.previousSibling)) {
			if (n.nodeType == 1 || (n.nodeType ==3 && !this.isEmpty(n))) {
				return n;
			}
		}
		return null;
	}

	this.isPrev = function(n, prev) {
		while ((n = this.prev(n))) {
			if (n == prev) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð²ÑÐµ ÑÐ»ÐµÐ´ÑƒÑŽÑ‰Ð¸Ðµ ÑÐ¾ÑÐµÐ´Ð½Ð¸Ð¸ Ð½Ð¾Ð´Ñ‹ (Ð½Ðµ Ð²ÐºÐ»ÑŽÑ‡Ð°ÑŽÑ‚ÑÑ Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ðµ Ð½Ð¾Ð´Ñ‹ Ð½Ðµ ÑÐ¾Ð·Ð´Ð°ÑŽÑ‰Ð¸Ðµ Ð·Ð½Ð°Ñ‡Ð¸Ð¼Ñ‹Ðµ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ñ‹ Ð¼ÐµÐ¶Ð´Ñƒ Ð¸Ð½Ð»Ð°Ð¹Ð½ ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ð°Ð¼Ð¸)
	 *
	 * @param  DOMElement n  Ð½Ð¾Ð´Ð°
	 * @return Array
	 **/
	this.nextAll = function(n) {
		var ret = [];
		while ((n = this.next(n))) {
			ret.push(n);
		}
		return ret;
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð²ÑÐµ Ð¿Ñ€ÐµÐ´Ñ‹Ð´ÑƒÑŽÑ‰Ð¸Ðµ ÑÐ¾ÑÐµÐ´Ð½Ð¸Ð¸ Ð½Ð¾Ð´Ñ‹ (Ð½Ðµ Ð²ÐºÐ»ÑŽÑ‡Ð°ÑŽÑ‚ÑÑ Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ðµ Ð½Ð¾Ð´Ñ‹ Ð½Ðµ ÑÐ¾Ð·Ð´Ð°ÑŽÑ‰Ð¸Ðµ Ð·Ð½Ð°Ñ‡Ð¸Ð¼Ñ‹Ðµ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ñ‹ Ð¼ÐµÐ¶Ð´Ñƒ Ð¸Ð½Ð»Ð°Ð¹Ð½ ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ð°Ð¼Ð¸)
	 *
	 * @param  DOMElement n  Ð½Ð¾Ð´Ð°
	 * @return Array
	 **/
	this.prevAll = function(n) {
		var ret = [];
		while ((n = this.prev(n))) {
			ret.push(n);
		}
		return ret;
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð²ÑÐµ ÑÐ»ÐµÐ´ÑƒÑŽÑ‰Ð¸Ðµ ÑÐ¾ÑÐµÐ´Ð½Ð¸Ð¸ inline Ð½Ð¾Ð´Ñ‹ (Ð½Ðµ Ð²ÐºÐ»ÑŽÑ‡Ð°ÑŽÑ‚ÑÑ Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ðµ Ð½Ð¾Ð´Ñ‹ Ð½Ðµ ÑÐ¾Ð·Ð´Ð°ÑŽÑ‰Ð¸Ðµ Ð·Ð½Ð°Ñ‡Ð¸Ð¼Ñ‹Ðµ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ñ‹ Ð¼ÐµÐ¶Ð´Ñƒ Ð¸Ð½Ð»Ð°Ð¹Ð½ ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ð°Ð¼Ð¸)
	 *
	 * @param  DOMElement n  Ð½Ð¾Ð´Ð°
	 * @return Array
	 **/
	this.toLineEnd = function(n) {
		var ret = [];
		while ((n = this.next(n)) && n.nodeName != 'BR' && n.nodeName != 'HR' && this.isInline(n)) {
			ret.push(n);
		}
		return ret;
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð²ÑÐµ Ð¿Ñ€ÐµÐ´Ñ‹Ð´ÑƒÑŽÑ‰Ð¸Ðµ ÑÐ¾ÑÐµÐ´Ð½Ð¸Ð¸ inline Ð½Ð¾Ð´Ñ‹ (Ð½Ðµ Ð²ÐºÐ»ÑŽÑ‡Ð°ÑŽÑ‚ÑÑ Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ðµ Ð½Ð¾Ð´Ñ‹ Ð½Ðµ ÑÐ¾Ð·Ð´Ð°ÑŽÑ‰Ð¸Ðµ Ð·Ð½Ð°Ñ‡Ð¸Ð¼Ñ‹Ðµ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ñ‹ Ð¼ÐµÐ¶Ð´Ñƒ Ð¸Ð½Ð»Ð°Ð¹Ð½ ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ð°Ð¼Ð¸)
	 *
	 * @param  DOMElement n  Ð½Ð¾Ð´Ð°
	 * @return Array
	 **/
	this.toLineStart = function(n) {
		var ret = [];
		while ((n = this.prev(n)) && n.nodeName != 'BR' && n.nodeName != 'HR' && this.isInline(n) ) {
			ret.unshift(n);
		}
		return ret;
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ TRUE, ÐµÑÐ»Ð¸ Ð½Ð¾Ð´Ð° - Ð¿ÐµÑ€Ð²Ñ‹Ð¹ Ð½ÐµÐ¿ÑƒÑÑ‚Ð¾Ð¹ ÑÐ»-Ñ‚ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ
	 *
	 * @param  Element n  Ð½Ð¾Ð´Ð°
	 * @return bool
	 **/
	this.isFirstNotEmpty = function(n) {
		while ((n = this.prev(n))) {
			if (n.nodeType == 1 || (n.nodeType == 3 && $.trim(n.nodeValue)!='' ) ) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ TRUE, ÐµÑÐ»Ð¸ Ð½Ð¾Ð´Ð° - Ð¿Ð¾ÑÐ»ÐµÐ´Ð½Ð¸Ð¹ Ð½ÐµÐ¿ÑƒÑÑ‚Ð¾Ð¹ ÑÐ»-Ñ‚ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ
	 *
	 * @param  Element n  Ð½Ð¾Ð´Ð°
	 * @return bool
	 **/
	this.isLastNotEmpty = function(n) {
		while ((n = this.next(n))) {
			if (!this.isEmpty(n)) {
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ TRUE, ÐµÑÐ»Ð¸ Ð½Ð¾Ð´Ð° - ÐµÐ´Ð¸Ð½ÑÑ‚Ð²ÐµÐ½Ð½Ñ‹Ð¹ Ð½ÐµÐ¿ÑƒÑÑ‚Ð¾Ð¹ ÑÐ»-Ñ‚ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ
	 *
	 * @param  DOMElement n  Ð½Ð¾Ð´Ð°
	 * @return bool
	 **/
	this.isOnlyNotEmpty = function(n) {
		return this.isFirstNotEmpty(n) && this.isLastNotEmpty(n);
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð¿Ð¾ÑÐ»ÐµÐ´Ð½Ð¸Ð¹ Ð½ÐµÐ¿ÑƒÑÑ‚Ð¾Ð¹ Ð´Ð¾Ñ‡ÐµÑ€Ð½Ð¸Ð¹ ÑÐ»-Ñ‚ Ð½Ð¾Ð´Ñ‹ Ð¸Ð»Ð¸ FALSE
	 *
	 * @param  Element n  Ð½Ð¾Ð´Ð°
	 * @return Element
	 **/
	this.findLastNotEmpty = function(n) {
		this.rte.log('findLastNotEmpty Who is here 0_o');
		if (n.nodeType == 1 && (l = n.lastChild)) {
			if (!this.isEmpty(l)) {
				return l;
			}
			while (l.previousSibling && (l = l.previousSibling)) {
				if (!this.isEmpty(l)) {
					return l;
				}
			}
		}
		return false;
	}
	
	/**
	 * Ð’Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ TRUE, ÐµÑÐ»Ð¸ Ð½Ð¾Ð´Ð° "inline" 
	 *
	 * @param  DOMElement n  Ð½Ð¾Ð´Ð°
	 * @return bool
	 **/
	this.isInline = function(n) {
		if (n.nodeType == 3) {
			return true;
		} else if (n.nodeType == 1) {
			n = $(n);
			var d = n.css('display');
			var f = n.css('float');
			return d == 'inline' || d == 'inline-block' || f == 'left' || f == 'right';
		}
		return true;
	}
	
	
	/********************************************************/
	/*                  ÐŸÐ¾Ð¸ÑÐº ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ð¾Ð²                     */
	/********************************************************/
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚(Ñ‹) Ð¾Ñ‚Ð²ÐµÑ‡Ð°ÑŽÑ‰Ð¸Ðµ ÑƒÑÐ»Ð¾Ð²Ð¸ÑÐ¼ Ð¿Ð¾Ð¸ÑÐºÐ°
	 *
	 * @param  DOMElement||Array  n       Ð½Ð¾Ð´Ð°
	 * @param  RegExp||String     filter  Ñ„Ð¸Ð»ÑŒÑ‚Ñ€ ÑƒÑÐ»Ð¾Ð²Ð¸Ñ Ð¿Ð¾Ð¸ÑÐºÐ° (RegExp Ð¸Ð»Ð¸ Ð¸Ð¼Ñ ÐºÐ»ÑŽÑ‡Ð° this.regExp Ð¸Ð»Ð¸ *)
	 * @return DOMElement||Array
	 **/
	this.filter = function(n, filter) {
		
		filter = this.regExp[filter] || filter;
		if (!n.push) {
			return n.nodeName && filter.test(n.nodeName) ? n : null;
		}
		var ret = [];
		for (var i=0; i < n.length; i++) {
			if (n[i].nodeName && n[i].nodeName && filter.test(n[i].nodeName)) {
				ret.push(n[i]);
			}
		};
		return ret;
	}
	
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð¼Ð°ÑÑÐ¸Ð² Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÑÐºÐ¸Ñ… ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ð¾Ð², Ð¾Ñ‚Ð²ÐµÑ‡Ð°ÑŽÑ‰Ð¸Ñ… ÑƒÑÐ»Ð¾Ð²Ð¸ÑÐ¼ Ð¿Ð¾Ð¸ÑÐºÐ°
	 *
	 * @param  DOMElement      n  Ð½Ð¾Ð´Ð°, Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÐµÐ¹, ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð¹ Ð¸Ñ‰ÐµÐ¼
	 * @param  RegExp||String  filter   Ñ„Ð¸Ð»ÑŒÑ‚Ñ€ ÑƒÑÐ»Ð¾Ð²Ð¸Ñ Ð¿Ð¾Ð¸ÑÐºÐ° (RegExp Ð¸Ð»Ð¸ Ð¸Ð¼Ñ ÐºÐ»ÑŽÑ‡Ð° this.regExp Ð¸Ð»Ð¸ *)
	 * @return Array
	 **/
	this.parents = function(n, filter) {
		var ret = [];
		filter = filter == '*' ? /.?/ : (this.regExp[filter] || filter);
			filter = this.regExp[filter] || filter;
			while (n && (n = n.parentNode) && n.nodeName != 'BODY' && n.nodeName != 'HTML') {
				if (filter.test(n.nodeName)) {
					ret.push(n);
				}
			}

		return ret;
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð±Ð»Ð¸Ð¶Ð°Ð¹ÑˆÐ¸Ð¹ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÑÐºÐ¸Ð¹ ÑÐ»-Ñ‚, Ð¾Ñ‚Ð²ÐµÑ‡Ð°ÑŽÑ‰Ð¸Ð¹ ÑƒÑÐ»Ð¾Ð²Ð¸ÑÐ¼ Ð¿Ð¾Ð¸ÑÐºÐ°
	 *
	 * @param  DOMElement     n  Ð½Ð¾Ð´Ð°, Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ, ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð¹ Ð¸Ñ‰ÐµÐ¼
	 * @param  RegExp||String f   Ñ„Ð¸Ð»ÑŒÑ‚Ñ€ ÑƒÑÐ»Ð¾Ð²Ð¸Ñ Ð¿Ð¾Ð¸ÑÐºÐ° (RegExp Ð¸Ð»Ð¸ Ð¸Ð¼Ñ ÐºÐ»ÑŽÑ‡Ð° this.regExp Ð¸Ð»Ð¸ *)
	 * @return DOMElement
	 **/
	this.parent = function(n, f) { 
		return this.parents(n, f)[0] || null; 
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð¸Ð»Ð¸ ÑÐ°Ð¼Ñƒ Ð½Ð¾Ð´Ñƒ Ð¸Ð»Ð¸ ÐµÐµ Ð±Ð»Ð¸Ð¶Ð°Ð¹ÑˆÐµÐ³Ð¾ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ, ÐµÑÐ»Ð¸ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÑÑŽÑ‚ÑÑ ÑƒÑÐ»Ð¾Ð²Ð¸Ñ sf Ð´Ð»Ñ ÑÐ°Ð¼Ð¾Ð¹ Ð½Ð¾Ð´Ñ‹ Ð¸Ð»Ð¸ pf Ð´Ð»Ñ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ
	 *
	 * @param  DOMElement     n  Ð½Ð¾Ð´Ð°, Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ, ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð¹ Ð¸Ñ‰ÐµÐ¼
	 * @param  RegExp||String sf   Ñ„Ð¸Ð»ÑŒÑ‚Ñ€ ÑƒÑÐ»Ð¾Ð²Ð¸Ñ Ð´Ð»Ñ ÑÐ°Ð¼Ð¾Ð¹ Ð½Ð¾Ð´Ñ‹
	* @param  RegExp||String  pf   Ñ„Ð¸Ð»ÑŒÑ‚Ñ€ ÑƒÑÐ»Ð¾Ð²Ð¸Ñ Ð´Ð»Ñ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ
	 * @return DOMElement
	 **/
	this.selfOrParent = function(n, sf, pf) {
		return this.filter(n, sf) || this.parent(n, pf||sf);
	}
	
	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÑÐºÑƒÑŽ Ð½Ð¾Ð´Ñƒ - ÑÑÑ‹Ð»ÐºÑƒ
	 *
	 * @param  Element n  Ð½Ð¾Ð´Ð°
	 * @return Element
	 **/
	this.selfOrParentLink = function(n) {
		n = this.selfOrParent(n, /^A$/);
		return n && n.href ? n : null;
	}

	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ TRUE, ÐµÑÐ»Ð¸ Ð½Ð¾Ð´Ð° -  anchor
	 *
	 * @param  Element n  Ð½Ð¾Ð´Ð°
	 * @return bool
	 **/
	this.selfOrParentAnchor = function(n) {
		n = this.selfOrParent(n, /^A$/);
		return n && !n.href && n.name ? n : null;
	}

	/**
	 * Ð’Ð¾Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð¼Ð°ÑÑÐ¸Ð² Ð´Ð¾Ñ‡ÐµÑ€Ð½Ð¸Ñ… ÑÑÑ‹Ð»Ð¾Ðº
	 *
	 * @param  DOMElement n  Ð½Ð¾Ð´Ð°
	 * @return Array
	 **/
	this.childLinks = function(n) {
		var res = [];
		$('a[href]', n).each(function() { res.push(this); });
		return res;
	}
	
	
	/********************************************************/
	/*                    Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ DOM                     */
	/********************************************************/
	
	/**
	 * ÐžÐ±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÑ‚ Ð¾Ð´Ð½Ñƒ Ð½Ð¾Ð´Ñƒ Ð´Ñ€ÑƒÐ³Ð¾Ð¹
	 *
	 * @param  DOMElement n  Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÐ¼Ð°Ñ Ð½Ð¾Ð´Ð°
	 * @param  DOMElement w  Ð½Ð¾Ð´Ð° Ð¾Ð±ÐµÑ€Ñ‚ÐºÐ° Ð¸Ð»Ð¸ Ð¸Ð¼Ñ Ñ‚ÐµÐ³Ð°
	 * @return DOMElement
	 **/
	this.wrap = function(n, w) {
		n = n.length ? n : [n];
		w = w.nodeName ? w : this.create(w);
		w = n[0].parentNode.insertBefore(w, n[0]);
		$(n).each(function() {
			if (this!=w) {
				w.appendChild(this);
			}
		})
		return w;
	}
	
	/**
	 * ÐžÐ±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÑ‚ Ð²ÑÐµ ÑÐ¾Ð´ÐµÑ€Ð¶Ð¸Ð¼Ð¾Ðµ Ð½Ð¾Ð´Ñ‹
	 *
	 * @param  DOMElement n  Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÐ¼Ð°Ñ Ð½Ð¾Ð´Ð°
	 * @param  DOMElement w  Ð½Ð¾Ð´Ð° Ð¾Ð±ÐµÑ€Ñ‚ÐºÐ° Ð¸Ð»Ð¸ Ð¸Ð¼Ñ Ñ‚ÐµÐ³Ð°
	 * @return DOMElement
	 **/
	this.wrapContents = function(n, w) {
		w = w.nodeName ? w : this.create(w);
		for (var i=0; i < n.childNodes.length; i++) {
			w.appendChild(n.childNodes[i]);
		};
		n.appendChild(w);
		return w;
	}
	
	this.cleanNode = function(n) {

		if (n.nodeType != 1) {
			return;
		}
		if (/^(P|LI)$/.test(n.nodeName) && (l = this.findLastNotEmpty(n)) && l.nodeName == 'BR') {
			$(l).remove();
		}
		$n = $(n);
		$n.children().each(function() {
			this.cleanNode(this);
		});
		if (n.nodeName != 'BODY' && !/^(TABLE|TR|TD)$/.test(n) && this.isEmpty(n)) {
			return $n.remove();
		}
		if ($n.attr('style') === '') {
			$n.removeAttr('style');
		}
		if (this.rte.browser.safari && $n.hasClass('Apple-span')) {
			$n.removeClass('Apple-span');
		}
		if (n.nodeName == 'SPAN' && !$n.attr('style') && !$n.attr('class') && !$n.attr('id')) {
			$n.replaceWith($n.html());
		}
	}
	
	this.cleanChildNodes = function(n) {
		var cmd = this.cleanNode;
		$(n).children().each(function() { cmd(this); });
	}
	
	/********************************************************/
	/*                       Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ‹                        */
	/********************************************************/
	
	this.tableMatrix = function(n) {
		var mx = [];
		if (n && n.nodeName == 'TABLE') {
			var max = 0;
			function _pos(r) {
				for (var i=0; i<=max; i++) {
					if (!mx[r][i]) {
						return i;
					}
				};
			}
			
			$(n).find('tr').each(function(r) {
				if (!$.isArray(mx[r])) {
					mx[r] = [];
				}
				
				$(this).children('td,th').each(function() {
					var w = parseInt($(this).attr('colspan')||1);
					var h = parseInt($(this).attr('rowspan')||1);
					var i = _pos(r);
					for (var y=0; y<h; y++) {
						for (var x=0; x<w; x++) {
							var _y = r+y;
							if (!$.isArray(mx[_y])) {
								mx[_y] = [];
							}
							var d = x==0 && y==0 ? this : (y==0 ? x : "-");
							mx[_y][i+x] = d;
						}
					};
					max= Math.max(max, mx[r].length);
				});
			});
		}
		return mx;
	}
	
	this.indexesOfCell = function(n, tbm) {
		for (var rnum=0; rnum < tbm.length; rnum++) {
			for (var cnum=0; cnum < tbm[rnum].length; cnum++) {
				if (tbm[rnum][cnum] == n) {
					return [rnum, cnum];
				}
				
			};
		};
	}
	
	this.fixTable = function(n) {
		if (n && n.nodeName == 'TABLE') {
			var tb = $(n);
			//tb.find('tr:empty').remove();
			var mx = this.tableMatrix(n);
			var x  = 0;
			$.each(mx, function() {
				x = Math.max(x, this.length);
			});
			if (x==0) {
				return tb.remove();
			}
			// for (var i=0; i<mx.length; i++) {
			// 	this.rte.log(mx[i]);
			// }
			
			for (var r=0; r<mx.length; r++) {
				var l = mx[r].length;
				//this.rte.log(r+' : '+l)
				
				if (l==0) {
					//this.rte.log('remove: '+tb.find('tr').eq(r))
					tb.find('tr').eq(r).remove();
//					tb.find('tr').eq(r).append('<td>remove</td>')
				} else if (l<x) {
					var cnt = x-l;
					var row = tb.find('tr').eq(r);
					for (i=0; i<cnt; i++) {
						row.append('<td>&nbsp;</td>');
					}
				}
			}
			
		}
	}
	
	this.tableColumn = function(n, ext, fix) {
		n      = this.selfOrParent(n, /^TD|TH$/);
		var tb = this.selfOrParent(n, /^TABLE$/);
		ret    = [];
		info   = {offset : [], delta : []};
		if (n && tb) {
			fix && this.fixTable(tb);
			var mx = this.tableMatrix(tb);
			var _s = false;
			var x;
			for (var r=0; r<mx.length; r++) {
				for (var _x=0; _x<mx[r].length; _x++) {
					if (mx[r][_x] == n) {
						x = _x;
						_s = true;
						break;
					}
				}
				if (_s) {
					break;
				}
			}
			
			// this.rte.log('matrix');
			// for (var i=0; i<mx.length; i++) {
			// 	this.rte.log(mx[i]);
			// }
			if (x>=0) {
				for(var r=0; r<mx.length; r++) {
					var tmp = mx[r][x]||null;
					if (tmp) {
						if (tmp.nodeName) {
							ret.push(tmp);
							if (ext) {
								info.delta.push(0);
								info.offset.push(x);
							}
						} else {
							var d = parseInt(tmp);
							if (!isNaN(d) && mx[r][x-d] && mx[r][x-d].nodeName) {
								ret.push(mx[r][x-d]);
								if (ext) {
									info.delta.push(d);
									info.offset.push(x);
								}
							}
						}
					}
				}
			}
		}
		return !ext ? ret : {column : ret, info : info};
	}
}

/*
 * elRTE configuration
 *
 * @param doctype         - doctype for editor iframe
 * @param cssClass        - css class for editor
 * @param cssFiles        - array of css files, witch will inlude in iframe
 * @param height          - not used now (may be deleted in future)
 * @param lang            - interface language (requires file in i18n dir)
 * @param toolbar         - name of toolbar to load
 * @param absoluteURLs    - convert files and images urls to absolute or not
 * @param allowSource     - is source editing allowing
 * @param stripWhiteSpace - strip Ð»Ð¸ÑˆÐ½Ð¸Ðµ whitespaces/tabs or not
 * @param styleWithCSS    - use style=... instead of strong etc.
 * @param fmAllow         - allow using file manger (elFinder)
 * @param fmOpen          - callback for open file manager
 * @param buttons         - object with pairs of buttons classes names and titles (when create new button, you have to add iys name here)
 * @param panels          - named groups of buttons
 * @param panelNames      - title of panels (required for one planned feature)
 * @param toolbars        - named redy to use toolbals (you may combine your own toolbar)
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 */
elRTE.prototype.options   = {
	doctype         : '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">',
	cssClass        : 'el-rte',
	cssfiles        : [],
	height          : null,
	lang            : 'en',
	toolbar         : 'normal',
	absoluteURLs    : true,
	allowSource     : true,
	stripWhiteSpace : false,
	styleWithCSS    : false,
	fmAllow         : true,
	fmOpen          : null,
	buttons         : {
		'save'                : 'Save',
		'copy'                : 'Copy',
		'cut'                 : 'Cut',
		'paste'               : 'Paste',
		'pastetext'           : 'Paste only text',
		'pasteformattext'     : 'Paste formatted text',
		'removeformat'        : 'Clean format', 
		'undo'                : 'Undo last action',
		'redo'                : 'Redo previous action',
		'bold'                : 'Bold',
		'italic'              : 'Italic',
		'underline'           : 'Underline',
		'strikethrough'       : 'Strikethrough',
		'superscript'         : 'Superscript',
		'subscript'           : 'Subscript',
		'justifyleft'         : 'Align left',
		'justifyright'        : 'Ailgn right',
		'justifycenter'       : 'Align center',
		'justifyfull'         : 'Align full',
		'indent'              : 'Indent',
		'outdent'             : 'Outdent',
		'forecolor'           : 'Font color',
		'hilitecolor'         : 'Background color',
		'formatblock'         : 'Format',
		'fontsize'            : 'Font size',
		'fontname'            : 'Font',
		'insertorderedlist'   : 'Ordered list',
		'insertunorderedlist' : 'Unordered list',
		'horizontalrule'      : 'Horizontal rule',
		'blockquote'          : 'Blockquote',
		'div'                 : 'Block element (DIV)',
		'link'                : 'Link',
		'unlink'              : 'Delete link',
		'anchor'              : 'Bookmark',
		'image'               : 'Image',
		'table'               : 'Table',
		'tablerm'             : 'Delete table',
		'tableprops'          : 'Table properties',
		'tbcellprops'         : 'Table cell properties',
		'tbrowbefore'         : 'Insert row before',
		'tbrowafter'          : 'Insert row after',
		'tbrowrm'             : 'Delete row',
		'tbcolbefore'         : 'Insert column before',
		'tbcolafter'          : 'Insert column after',
		'tbcolrm'             : 'Delete column',
		'tbcellsmerge'        : 'Merge table cells',
		'tbcellsplit'         : 'Split table cell',
		'docstructure'        : 'Toggle display document structure',
		'elfinder'            : 'Open file manager',
		'fullscreen'          : 'Toggle full screen mode',
		'nbsp'                : 'Non breakable space',
		'stopfloat'           : 'Stop element floating'
	},
	panels      : {
		save       : ['save'],
		copypaste  : ['copy', 'cut', 'paste', 'pastetext', 'pasteformattext', 'removeformat', 'docstructure'],
		undoredo   : ['undo', 'redo'],
		style      : ['bold', 'italic', 'underline', 'strikethrough', 'subscript', 'superscript'],
		colors     : ['forecolor', 'hilitecolor'],
		alignment  : ['justifyleft', 'justifycenter', 'justifyright', 'justifyfull'],
		indent     : ['outdent', 'indent'],
		format     : ['formatblock', 'fontsize', 'fontname'],
		lists      : ['insertorderedlist', 'insertunorderedlist'],
		elements   : ['horizontalrule', 'blockquote', 'div', 'stopfloat', 'nbsp'],
		links      : ['link', 'unlink', 'anchor'],
		images     : ['image'],
		media      : ['image'],		
		tables     : ['table', 'tableprops', 'tablerm',  'tbrowbefore', 'tbrowafter', 'tbrowrm', 'tbcolbefore', 'tbcolafter', 'tbcolrm', 'tbcellprops', 'tbcellsmerge', 'tbcellsplit'],
		elfinder   : ['elfinder'],
		fullscreen : ['fullscreen']
	},
	toolbars    : {
		tiny     : ['style'],
		compact  : ['save', 'undoredo', 'style', 'alignment', 'lists', 'links', 'fullscreen'],
		normal   : ['save', 'copypaste', 'undoredo', 'style', 'alignment', 'colors', 'indent', 'lists', 'links', 'elements', 'images', 'fullscreen'],
		complite : ['save', 'copypaste', 'undoredo', 'style', 'alignment', 'colors', 'format', 'indent', 'lists', 'links', 'elements', 'media', 'fullscreen'],
		maxi     : ['save', 'copypaste', 'undoredo', 'style', 'alignment', 'colors', 'format', 'indent', 'lists', 'links', 'elements', 'media', 'tables', 'fullscreen'],
		eldorado : ['save', 'copypaste', 'elfinder', 'undoredo', 'style', 'alignment', 'colors', 'format', 'indent', 'lists', 'links', 'elements', 'media', 'tables', 'fullscreen']
		
	},
	panelNames : {
		save      : 'Save',
		copypaste : 'Copy/Pase',
		undoredo  : 'Undo/Redo',
		style     : 'Text styles',
		colors    : 'Colors',
		alignment : 'Alignment',
		indent    : 'Indent/Outdent',
		format    : 'Text format',
		lists     : 'Lists',
		elements  : 'Misc elements',
		links     : 'Links',
		images    : 'Images',
		media     : 'Media',
		tables    : 'Tables',
		elfinder  : 'File manager (elFinder)'
	}
};
/**
 * @class selection  - elRTE utils for working with text selection
 *
 * @param  elRTE  rte  Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/

elRTE.prototype.selection = function(rte) {
	this.rte      = rte;
	var self      = this;
	this.w3cRange = null;
	var start, end, node, bm;
	
	$(this.rte.doc)
		.keyup(function(e) {
			if (e.ctrlKey || e.metaKey || (e.keyCode >= 8 && e.keyCode <= 13) || (e.keyCode>=32 && e.keyCode<= 40) || e.keyCode == 46 || (e.keyCode >=96 && e.keyCode <= 111)) {
				self.cleanCache();
			}
		})
		.mousedown(function(e) {
			if (e.target.nodeName == 'HTML') {
				start = self.rte.doc.body;
			} else {
				start = e.target;
			}
			end   = node = null;
		})
		.mouseup(function(e) {
			if (e.target.nodeName == 'HTML') {
				end = self.rte.doc.body;
			} else {
				end = e.target;
			}
			end  = e.target;
			node = null;
		}).click();
		
	/**
	 * Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ selection
	 *
	 * @return  Selection
	 **/
	function selection() {
		return self.rte.window.getSelection ? self.rte.window.getSelection() : self.rte.window.document.selection;
	}
	
	/**
	 * Ð’ÑÐ¿Ð¾Ð¼Ð¾Ð³Ð°Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ
	 * Ð’Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ ÑÐ°Ð¼Ð¾Ð³Ð¾ Ð²ÐµÑ€Ñ…Ð½ÐµÐ³Ð¾ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ, Ð¾Ñ‚Ð²ÐµÑ‡Ð°ÑŽÑ‰ÐµÐ³Ð¾ ÑƒÑÐ»Ð¾Ð²Ð¸ÑŽ - Ñ‚ÐµÐºÑƒÑ‰Ð°Ñ Ð½Ð¾Ð´Ð° - ÐµÐ³Ð¾ ÐµÐ´Ð¸Ð½ÑÑ‚Ð²ÐµÐ½Ð½Ð°Ñ Ð½ÐµÐ¿ÑƒÑÑ‚Ð°Ñ Ð´Ð¾Ñ‡ÐµÑ€Ð½ÑÑ Ð½Ð¾Ð´Ð°
	 *
	 * @param   DOMElement  n Ð½Ð¾Ð´Ð°, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð¹ Ð¸Ñ‰ÐµÐ¼ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»Ñ
	 * @param   DOMElement  p ÐµÑÐ»Ð¸ Ð·Ð°Ð´Ð°Ð½Ð° - Ð½Ð¾Ð´Ð°, Ð²Ñ‹ÑˆÐµ ÐºÐ¾Ñ‚Ð¾Ñ€Ð¾Ð¹ Ð½Ðµ Ð¿Ð¾Ð´Ð½Ð¸Ð¼Ð°ÐµÐ¼ÑÑ
	 * @param   String      s ÑÑ‚Ñ€Ð¾Ð½Ð° Ð¿Ð¾Ð¸ÑÐºÐ° (left||right||null)
	 * @return  DOMElement
	 **/
	function realSelected(n, p, s) {
		while (n.nodeName != 'BODY' && n.parentNode && n.parentNode.nodeName != 'BODY' && (p ? n!== p && n.parentNode != p : 1) && ((s=='left' && self.rte.dom.isFirstNotEmpty(n)) || (s=='right' && self.rte.dom.isLastNotEmpty(n)) || (self.rte.dom.isFirstNotEmpty(n) && self.rte.dom.isLastNotEmpty(n))) ) {
			n = n.parentNode;
		}
		return n;
	}
	
	/**
	 * Ð’Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ TRUE, ÐµÑÐ»Ð¸ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ "ÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚Ð¾"
	 *
	 * @return  bool
	 **/
	this.collapsed = function() {
		return this.getRangeAt().isCollapsed();
	}
	
	/**
	 * "Ð¡Ñ…Ð»Ð¾Ð¿Ñ‹Ð²Ð°ÐµÑ‚" Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ 
	 *
	 * @param   bool  toStart  ÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚ÑŒ Ðº Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¾Ð¹ Ñ‚Ð¾Ñ‡ÐºÐµ
	 * @return  void
	 **/
	this.collapse = function(toStart) {
		this.getRangeAt().collapse(toStart ? true : false);
	}
	
	/**
	 * Ð’Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ TextRange
	 * Ð”Ð»Ñ Ð½Ð¾Ñ€Ð¼Ð°Ð»ÑŒÐ½Ñ‹Ñ… Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð¾Ð² - Ð½Ð°Ñ‚Ð¸Ð²Ð½Ñ‹Ð¹ range
	 * Ð´Ð»Ñ "ÑÐ°Ð¼Ð¸Ð·Ð½Ð°ÐµÑ‚ÐµÑ‡ÐµÐ³Ð¾" - ÑÐ¼ÑƒÐ»ÑÑ†Ð¸ÑŽ w3c range
	 *
	 * @return  range|w3cRange
	 **/
	this.getRangeAt = function(updateW3cRange) {
		if (this.rte.browser.msie) {
			if (!this.w3cRange) {
				this.w3cRange = new this.rte.w3cRange(this.rte);
			}
			updateW3cRange && this.w3cRange.update();
			return this.w3cRange;
		}
		
		var s = selection();
		var r = s.rangeCount > 0 ? s.getRangeAt(0) : this.rte.doc.createRange();
		r.getStart = function() {
			return this.startContainer.nodeType==1 
				? this.startContainer.childNodes[Math.min(this.startOffset, this.startContainer.childNodes.length-1)] 
				: this.startContainer;
		}
		
		r.getEnd = function() {
			return this.endContainer.nodeType==1 
				? this.endContainer.childNodes[ Math.min(this.startOffset == this.endOffset ? this.endOffset : this.endOffset-1, this.endContainer.childNodes.length-1)] 
				: this.endContainer;
		}
		r.isCollapsed = function() {
			return this.collapsed;
		}
		return r;
	}
	
	this.saveIERange = function() {
		if ($.browser.msie) {
			bm = this.getRangeAt().getBookmark();
		}
	}
	
	this.restoreIERange = function() {
		$.browser.msie && bm && this.getRangeAt().moveToBookmark(bm);
	}
	
	/**
	 * Ð’Ñ‹Ð´ÐµÐ»ÑÐµÑ‚ Ð½Ð¾Ð´Ñ‹
	 *
	 * @param   DOMNode  s  Ð½Ð¾Ð´Ð° Ð½Ð°Ñ‡Ð°Ð»Ð° Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ
	 * @param   DOMNode  e  Ð½Ð¾Ð´Ð° ÐºÐ¾Ð½Ñ†Ð° Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ
	 * @return  selection
	 **/
	this.select = function(s, e) {
		e = e||s;
		var r = this.getRangeAt();
		r.setStartBefore(s);
		r.setEndAfter(e);
		if (this.rte.browser.msie) {
			r.select();
		} else {
			var s = selection();
			s.removeAllRanges();
			s.addRange(r);
		}
		return this.cleanCache();
	}
	
	/**
	 * Ð’Ñ‹Ð´ÐµÐ»ÑÐµÑ‚ ÑÐ¾Ð´ÐµÑ€Ð¶Ð¸Ð¼Ð¾Ðµ Ð½Ð¾Ð´Ñ‹
	 *
	 * @param   Element  n  Ð½Ð¾Ð´Ð°
	 * @return  selection
	 **/
	this.selectContents = function(n) {
		var r = this.getRangeAt();
		if (n && n.nodeType == 1) {
			if (this.rte.browser.msie) {
				r.range();
				r.r.moveToElementText(n.parentNode);
				r.r.select();
			} else {
				try {
					r.selectNodeContents(n);
				} catch (e) {
					return this.rte.log('unable select node contents '+n);
				}
				var s = selection();
				s.removeAllRanges();
				s.addRange(r);
			}
		}
		return this;
	}
	
	/**
	 * Ð’ÑÑ‚Ð°Ð²Ð»ÑÐµÑ‚ Ð½Ð¾Ð´Ñƒ Ð² Ñ‚ÐµÐºÑƒÑ‰ÐµÐµ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ
	 *
	 * @param   Element  n  Ð½Ð¾Ð´Ð°
	 * @return  selection
	 **/
	this.insertNode = function(n, collapse) {
		if (collapse && !this.collapsed()) {
			this.collapse();
		}

		if (this.rte.browser.msie) {
			var html = n.nodeType == 3 ? n.nodeValue : $(this.rte.dom.create('span')).append($(n)).html();
			 var r = this.getRangeAt();
			r.insertNode(html);
		} else {
			var r = this.getRangeAt();
			r.insertNode(n);
			r.setStartAfter(n);
			r.setEndAfter(n);
			var s = selection();
			s.removeAllRanges();
			s.addRange(r);
		}
		return this.cleanCache();
	}

	/**
	 * Ð’ÑÑ‚Ð°Ð²Ð»ÑÐµÑ‚ html Ð² Ñ‚ÐµÐºÑƒÑ‰ÐµÐµ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ
	 *
	 * @param   Element  n  Ð½Ð¾Ð´Ð°
	 * @return  selection
	 **/
	this.insertHtml = function(html, collapse) {
		if (collapse && !this.collapsed()) {
			this.collapse();
		}
		
		if (this.rte.browser.msie) {
			this.getRangeAt().range().pasteHTML(html);
		} else {
			var n = $(this.rte.dom.create('span')).html(html||'').get(0);
			this.insertNode(n);
			$(n).replaceWith($(n).html());
		}
		return this.cleanCache();
	}

	/**
	 * Ð’ÑÑ‚Ð°Ð²Ð»ÑÐµÑ‚ Ð½Ð¾Ð´Ñƒ Ð² Ñ‚ÐµÐºÑƒÑ‰ÐµÐµ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ
	 *
	 * @param   Element  n  Ð½Ð¾Ð´Ð°
	 * @return  selection
	 **/
	this.insertText = function(text, collapse) {
		var n = this.rte.doc.createTextNode(text);
		return this.insertHtml(n.nodeValue);
	}

	/**
	 * ÐžÑ‡Ð¸Ñ‰Ð°ÐµÑ‚ ÐºÑÑˆ
	 *
	 * @return  selection
	 **/
	this.cleanCache = function() {
		start = end = node = null;
		return this;
	}

	
	/**
	 * Ð’Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð½Ð¾Ð´Ñƒ Ð½Ð°Ñ‡Ð°Ð»Ð° Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ
	 *
	 * @return  DOMElement
	 **/
	this.getStart = function() {
		if (!start) {
			var r = this.getRangeAt();
			start = r.getStart();
		}
		return start;
	}
	
	/**
	 * Ð’Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð½Ð¾Ð´Ñƒ ÐºÐ¾Ð½Ñ†Ð° Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ
	 *
	 * @return  DOMElement
	 **/
	this.getEnd = function() {
		if (!end) {
			var r = this.getRangeAt();
			end = r.getEnd();
		}
		return end;
	}

	/**
	 * Ð’Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð²Ñ‹Ð±Ñ€Ð°Ð½ÑƒÑŽ Ð½Ð¾Ð´Ñƒ (Ð¾Ð±Ñ‰Ð¸Ð¹ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€ Ð²ÑÐµÑ… Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ð½Ð¾Ð´)
	 *
	 * @return  Element
	 **/
	this.getNode = function() {
		if (!node) {
			node = this.rte.dom.findCommonAncestor(this.getStart(), this.getEnd());
		}
		return node;
	}

	
	/**
	 * Ð’Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð¼Ð°ÑÑÐ¸Ð² Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ð½Ð¾Ð´
	 *
	 * @param   Object  o  Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ñ‹ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ñ‹Ñ… Ð½Ð¾Ð´
	 * @return  Array
	 **/
	this.selected = function(o) {
		var opts = {
			collapsed : false,  // Ð²ÐµÑ€Ð½ÑƒÑ‚ÑŒ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ, Ð´Ð°Ð¶Ðµ ÐµÑÐ»Ð¸ Ð¾Ð½Ð¾ ÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚Ð¾
			blocks    : false,  // Ð±Ð»Ð¾Ñ‡Ð½Ð¾Ðµ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ
			filter    : false,  // Ñ„Ð¸Ð»ÑŒÑ‚Ñ€ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚Ð¾Ð²
			wrap      : 'text', // Ñ‡Ñ‚Ð¾ Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÐ¼
			tag       : 'span'  // Ð²Ð¾ Ñ‡Ñ‚Ð¾ Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÐ¼
		}
		opts = $.extend({}, opts, o);
		
		// Ð±Ð»Ð¾Ñ‡Ð½Ð¾Ðµ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ - Ð¸Ñ‰ÐµÐ¼ Ð±Ð»Ð¾Ñ‡Ð½ÑƒÑŽ Ð½Ð¾Ð´Ñƒ, Ð½Ð¾ Ð½Ðµ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñƒ
		if (opts.blocks) {
			var n  = this.getNode(), _n = null;
			if (_n = this.rte.dom.selfOrParent(n, 'selectionBlock') ) {
				return [_n];
			} 
		}

		var sel    = this.selectedRaw(opts.collapsed, opts.blocks);
		var ret    = [];
		var buffer = [];
		var ndx    = null;

		// Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÐ¼ Ð½Ð¾Ð´Ñ‹ Ð² Ð±ÑƒÑ„Ñ„ÐµÑ€Ðµ
		function wrap() {
			
			function allowParagraph() {
				for (var i=0; i < buffer.length; i++) {
					if (buffer[i].nodeType == 1 && (self.rte.dom.selfOrParent(buffer[i], /^P$/) || $(buffer[i]).find('p').length>0)) {
						return false;
					}
				};
				return true;
			} 
			
			if (buffer.length>0) {
				var tag  = opts.tag == 'p' && !allowParagraph() ? 'div' : opts.tag;
				var n    = self.rte.dom.wrap(buffer, tag);
				ret[ndx] = n;
				ndx      = null;
				buffer   = [];
			}
		}
		
		// Ð´Ð¾Ð±Ð°Ð²Ð»ÑÐµÐ¼ Ð½Ð¾Ð´Ñ‹ Ð² Ð±ÑƒÑ„Ñ„ÐµÑ€
		function addToBuffer(n) {
			if (n.nodeType == 1) {
				if (/^(THEAD|TFOOT|TBODY|COL|COLGROUP|TR)$/.test(n.nodeName)) {
					$(n).find('td,th').each(function() {
						var tag = opts.tag == 'p' && $(this).find('p').length>0 ? 'div' : opts.tag;
						var n = self.rte.dom.wrapContents(this, tag);
						return ret.push(n);
					})
				} else if (/^(CAPTION|TD|TH|LI|DT|DD)$/.test(n.nodeName)) {
					var tag = opts.tag == 'p' && $(n).find('p').length>0 ? 'div' : opts.tag;
					var n = self.rte.dom.wrapContents(n, tag);
					return ret.push(n);
				} 
			} 
			var prev = buffer.length>0 ? buffer[buffer.length-1] : null;
			if (prev && prev != self.rte.dom.prev(n)) {
				wrap();
			}
			buffer.push(n); 
			if (ndx === null) {
				ndx = ret.length;
				ret.push('dummy'); // Ð·Ð°Ð³Ð»ÑƒÑˆÐºÐ° Ð´Ð»Ñ Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÐ¼Ñ‹Ñ… ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ð¾Ð²
			}
		}
		
		if (sel.nodes.length>0) {
			
			for (var i=0; i < sel.nodes.length; i++) {
				var n = sel.nodes[i];
					// Ð¿ÐµÑ€Ð²ÑƒÑŽ Ð¸ Ð¿Ð¾ÑÐ» Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ðµ Ð½Ð¾Ð´Ñ‹ Ñ€Ð°Ð·Ñ€ÐµÐ·Ð°ÐµÐ¼, ÐµÑÐ»Ð¸ Ð½ÐµÐ¾Ð±Ñ…Ð¾Ð´Ð¸Ð¼Ð¾
					 if (n.nodeType == 3 && (i==0 || i == sel.nodes.length-1) && $.trim(n.nodeValue).length>0) {
						if (i==0 && sel.so>0) {
							n = n.splitText(sel.so);
						}
						if (i == sel.nodes.length-1 && sel.eo>0) {
							n.splitText(i==0 && sel.so>0 ? sel.eo - sel.so : sel.eo);
						}
					}

					switch (opts.wrap) {
						// Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÐ¼ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ðµ Ð½Ð¾Ð´Ñ‹ Ñ br
						case 'text':
							if ((n.nodeType == 1 && n.nodeName == 'BR') || (n.nodeType == 3 && $.trim(n.nodeValue).length>0)) {
								addToBuffer(n);
							} else if (n.nodeType == 1) {
								ret.push(n);
							}
							break;
						// Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÐ¼ Ð²ÑÐµ Ð¸Ð½Ð»Ð°Ð¹Ð½ ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ñ‹	
						case 'inline':
							if (this.rte.dom.isInline(n)) {
								addToBuffer(n);
							} else if (n.nodeType == 1) {
								
								ret.push(n);
							}
							break;
						// Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÐ¼ Ð²ÑÐµ	
						case 'all':
							if (n.nodeType == 1 || !this.rte.dom.isEmpty(n)) {
								addToBuffer(n);
							}
							break;
						// Ð½Ð¸Ñ‡ÐµÐ³Ð¾ Ð½Ðµ Ð¾Ð±Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÐ¼
						default:
							if (n.nodeType == 1 || !this.rte.dom.isEmpty(n)) {
								ret.push(n);
							}
					}
			};
			wrap();
		}
		// this.rte.log('buffer')
		// this.rte.log(buffer)
		// this.rte.log('ret')
		// this.rte.log(ret)		
		return opts.filter ? this.rte.dom.filter(ret, opts.filter) : ret;
	}

	this.dump = function(ca, s, e, so, eo) {
		var r = this.getRangeAt();
		this.rte.log('commonAncestorContainer');
		this.rte.log(ca || r.commonAncestorContainer);
		// this.rte.log('commonAncestorContainer childs num')
		// this/rte.log((ca||r.commonAncestorContainer).childNodes.length)
		this.rte.log('startContainer');
		this.rte.log(s || r.startContainer);
		this.rte.log('startOffset: '+(so>=0 ? so : r.startOffset));
		this.rte.log('endContainer');
		this.rte.log(e||r.endContainer);
		this.rte.log('endOffset: '+(eo>=0 ? eo : r.endOffset));
	}

	/**
	 * Ð’Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ Ð¼Ð°ÑÑÐ¸Ð² Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… Ð½Ð¾Ð´, ÐºÐ°Ðº ÐµÑÑ‚ÑŒ
	 *
	 * @param   bool           Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°Ñ‚ÑŒ ÐµÑÐ»Ð¸ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ ÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚Ð¾
	 * @param   bool           "Ð±Ð»Ð¾Ñ‡Ð½Ð¾Ðµ" Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ (Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ðµ Ð½Ð¾Ð´Ñ‹ Ð²ÐºÐ»ÑŽÑ‡Ð°ÑŽÑ‚ÑÑ Ð¿Ð¾Ð»Ð½Ð¾ÑÑ‚ÑŒÑŽ, Ð½Ðµ Ð·Ð°Ð²Ð¸ÑÐ¸Ð¼Ð¾ Ð¾Ñ‚ offset)
	 * @return  Array
	 **/
	this.selectedRaw = function(collapsed, blocks) {
		var res = {so : null, eo : null, nodes : []};
		var r   = this.getRangeAt(true);
		var ca  = r.commonAncestorContainer;
		var s, e;  // start & end nodes
		var sf  = false; // start node fully selected
		var ef  = false; // end node fully selected
		
		// Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ true, ÐµÑÐ»Ð¸ Ð½Ð¾Ð´Ð° Ð½Ðµ Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ð°Ñ Ð¸Ð»Ð¸ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð° Ð¿Ð¾Ð»Ð½Ð¾ÑÑ‚ÑŒÑŽ
		function isFullySelected(n, s, e) {
			if (n.nodeType == 3) {
				e = e>=0 ? e : n.nodeValue.length;
				return (s==0 && e==n.nodeValue.length) || $.trim(n.nodeValue).length == $.trim(n.nodeValue.substring(s, e)).length;
			} 
			return true;
		}
		
		// Ð²Ð¾Ð·Ð²Ñ€Ð°Ñ‰Ð°ÐµÑ‚ true, ÐµÑÐ»Ð¸ Ð½Ð¾Ð´Ð° Ð¿ÑƒÑÑ‚Ð°Ñ Ð¸Ð»Ð¸ Ð² Ð½ÐµÐ¹ Ð½Ðµ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¾ Ð½Ð¸ Ð¾Ð´Ð½Ð¾Ð³Ð¾ Ð½ÐµÐ¿Ñ€Ð¾Ð±ÐµÐ»ÑŒÐ½Ð¾Ð³Ð¾ ÑÐ¸Ð¼Ð²Ð¾Ð»Ð°
		function isEmptySelected(n, s, e) {
			if (n.nodeType == 1) {
				return self.rte.dom.isEmpty(n);
			} else if (n.nodeType == 3) {
				return $.trim(n.nodeValue.substring(s||0, e>=0 ? e : n.nodeValue.length)).length == 0;
			} 
			return true;
		}
		
		
		//this.dump()
		// Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ð½Ð¾Ð´Ð°
		if (r.startContainer.nodeType == 1) {
			if (r.startOffset<r.startContainer.childNodes.length) {
				s = r.startContainer.childNodes[r.startOffset];
				res.so = s.nodeType == 1 ? null : 0;
			} else {
				s = r.startContainer.childNodes[r.startOffset-1];
				res.so = s.nodeType == 1 ? null : s.nodeValue.length;
			}
		} else {
			s = r.startContainer;
			res.so = r.startOffset;
		} 
		
		// Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ ÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚Ð¾
		if (r.collapsed) {
			if (collapsed) {
				//  Ð±Ð»Ð¾Ñ‡Ð½Ð¾Ðµ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ
				if (blocks) {
					s = realSelected(s);
					if (!this.rte.dom.isEmpty(s) || (s = this.rte.dom.next(s))) {
						res.nodes = [s];
					} 
					
					// Ð´Ð¾Ð±Ð°Ð²Ð»ÑÐµÐ¼ Ð¸Ð½Ð»Ð°Ð¹Ð½ ÑÐ¾ÑÐµÐ´ÐµÐ¹ 
					if (this.rte.dom.isInline(s)) {
						res.nodes = this.rte.dom.toLineStart(s).concat(res.nodes, this.rte.dom.toLineEnd(s));
					}
					
					// offset Ð´Ð»Ñ Ñ‚ÐµÐºÑÑ‚Ð¾Ð²Ñ‹Ñ… Ð½Ð¾Ð´
					if (res.nodes.length>0) {
						res.so = res.nodes[0].nodeType == 1 ? null : 0;
						res.eo = res.nodes[res.nodes.length-1].nodeType == 1 ? null : res.nodes[res.nodes.length-1].nodeValue.length;
					}
					
				} else if (!this.rte.dom.isEmpty(s)) {
					res.nodes = [s];
				}
				
			}
			return res;
		}
		
		// ÐºÐ¾Ð½ÐµÑ‡Ð½Ð°Ñ Ð½Ð¾Ð´Ð°
		if (r.endContainer.nodeType == 1) {
			e = r.endContainer.childNodes[r.endOffset-1];
			res.eo = e.nodeType == 1 ? null : e.nodeValue.length;
		} else {
			e = r.endContainer;
			res.eo = r.endOffset;
		} 
		// this.rte.log('select 1')
		//this.dump(ca, s, e, res.so, res.eo)
		
		// Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ð½Ð¾Ð´Ð° Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð° Ð¿Ð¾Ð»Ð½Ð¾ÑÑ‚ÑŒÑŽ - Ð¿Ð¾Ð´Ð½Ð¸Ð¼Ð°ÐµÐ¼ÑÑ Ð½Ð°Ð²ÐµÑ€Ñ… Ð¿Ð¾ Ð»ÐµÐ²Ð¾Ð¹ ÑÑ‚Ð¾Ñ€Ð¾Ð½Ðµ
		if (s.nodeType == 1 || blocks || isFullySelected(s, res.so, s.nodeValue.length)) {
//			this.rte.log('start text node is fully selected')
			s = realSelected(s, ca, 'left');
			sf = true;
			res.so = s.nodeType == 1 ? null : 0;
		}
		// ÐºÐ¾Ð½ÐµÑ‡Ð½Ð°Ñ Ð½Ð¾Ð´Ð° Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð° Ð¿Ð¾Ð»Ð½Ð¾ÑÑ‚ÑŒÑŽ - Ð¿Ð¾Ð´Ð½Ð¸Ð¼Ð°ÐµÐ¼ÑÑ Ð½Ð°Ð²ÐµÑ€Ñ… Ð¿Ð¾ Ð¿Ñ€Ð°Ð²Ð¾Ð¹ ÑÑ‚Ð¾Ñ€Ð¾Ð½Ðµ
		if (e.nodeType == 1 || blocks || isFullySelected(e, 0,  res.eo)) {
//			this.rte.log('end text node is fully selected')
			e = realSelected(e, ca, 'right');
			ef = true;
			res.eo = e.nodeType == 1 ? null : e.nodeValue.length;
		}

		// Ð±Ð»Ð¾Ñ‡Ð½Ð¾Ðµ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ - ÐµÑÐ»Ð¸ Ð½Ð¾Ð´Ñ‹ Ð½Ðµ ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ñ‹ - Ð¿Ð¾Ð´Ð½Ð¸Ð¼Ð°ÐµÐ¼ÑÑ Ðº Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŽ, Ð½Ð¾ Ð½Ð¸Ð¶Ðµ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ð°
		if (blocks) {
			if (s.nodeType != 1 && s.parentNode != ca && s.parentNode.nodeName != 'BODY') {
				s = s.parentNode;
				res.so = null;
			}
			if (e.nodeType != 1 && e.parentNode != ca && e.parentNode.nodeName != 'BODY') {
				e = e.parentNode;
				res.eo = null;
			}
		}

		// ÐµÑÐ»Ð¸ ÐºÐ¾Ð½Ñ‚ÐµÐ½ÐµÑ€ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½ Ð¿Ð¾Ð»Ð½Ð¾ÑÑ‚ÑŒÑŽ, Ð¿Ð¾Ð´Ð½Ð¸Ð¼Ð°ÐµÐ¼ÑÑ Ð½Ð°Ð²ÐµÑ€Ñ… Ð½Ð°ÑÐºÐ¾Ð»ÑŒÐºÐ¾ Ð¼Ð¾Ð¶Ð½Ð¾
		if (s.parentNode == e.parentNode && s.parentNode.nodeName != 'BODY' && (sf && this.rte.dom.isFirstNotEmpty(s)) && (ef && this.rte.dom.isLastNotEmpty(e))) {
//			this.rte.log('common parent')
			s = e = s.parentNode;
			res.so = s.nodeType == 1 ? null : 0;
			res.eo = e.nodeType == 1 ? null : e.nodeValue.length;
		}
		// Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ð½Ð¾Ð´Ð° == ÐºÐ¾Ð½ÐµÑ‡Ð½Ð¾Ð¹ Ð½Ð¾Ð´Ðµ
		if (s == e) {
//			this.rte.log('start is end')
			if (!this.rte.dom.isEmpty(s)) {
				res.nodes.push(s);
			}
			return res;
		}
		 // this.rte.log('start 2')
		  //this.dump(ca, s, e, res.so, res.eo)
		
		// Ð½Ð°Ñ…Ð¾Ð´Ð¸Ð¼ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½ÑƒÑŽ Ð¸ ÐºÐ¾Ð½ÐµÑ‡Ð½ÑƒÑŽ Ñ‚Ð¾Ñ‡ÐºÐ¸ - Ð½Ð¾Ð´Ñ‹ Ð¸Ð· Ð¸ÐµÑ€Ð°Ñ€Ñ…Ð¸Ð¸ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÐµÐ¹ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¾Ð¹ Ð¸ ÐºÐ¾Ð½ÐµÑ‡Ð½Ð¾ Ð½Ð¾Ð´Ñ‹, Ñƒ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒ - ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€
		var sp = s;
		while (sp.nodeName != 'BODY' && sp.parentNode !== ca && sp.parentNode.nodeName != 'BODY') {
			sp = sp.parentNode;
		}
		//this.rte.log(s.nodeName)
		// this.rte.log('start point')
		// this.rte.log(sp)
		
		var ep = e;
//		this.rte.log(ep)
		while (ep.nodeName != 'BODY' && ep.parentNode !== ca && ep.parentNode.nodeName != 'BODY') {
			this.rte.log(ep)
			ep = ep.parentNode;
		}
		// this.rte.log('end point')
		// this.rte.log(ep)
		
		
		//  ÐµÑÐ»Ð¸ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð°Ñ Ð½Ð¾Ð´Ð° Ð½Ðµ Ð¿ÑƒÑÑ‚Ð°Ñ - Ð´Ð¾Ð±Ð°Ð²Ð»ÑÐµÐ¼ ÐµÐµ
		if (!isEmptySelected(s, res.so, s.nodeType==3 ? s.nodeValue.length : null)) {
			res.nodes.push(s);
		}
		// Ð¿Ð¾Ð´Ð½Ð¸Ð¼Ð°ÐµÐ¼ÑÑ Ð¾Ñ‚ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¾Ð¹ Ð½Ð¾Ð´Ñ‹ Ð´Ð¾ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¾Ð¹ Ñ‚Ð¾Ñ‡ÐºÐ¸
		var n = s;
		while (n !== sp) {
			var _n = n;
			while ((_n = this.rte.dom.next(_n))) {
					res.nodes.push(_n);
			}
			n = n.parentNode;
		}
		// Ð¾Ñ‚ Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¾Ð¹ Ñ‚Ð¾Ñ‡ÐºÐ¸ Ð´Ð¾ ÐºÐ¾Ð½ÐµÑ‡Ð½Ð¾Ð¹ Ñ‚Ð¾Ñ‡ÐºÐ¸
		n = sp;
		while ((n = this.rte.dom.next(n)) && n!= ep ) {
//			this.rte.log(n)
			res.nodes.push(n);
		}
		// Ð¿Ð¾Ð´Ð½Ð¸Ð¼Ð°ÐµÐ¼ÑÑ Ð¾Ñ‚ ÐºÐ¾Ð½ÐµÑ‡Ð½Ð¾Ð¹ Ð½Ð¾Ð´Ñ‹ Ð´Ð¾ ÐºÐ¾Ð½ÐµÑ‡Ð½Ð¾Ð¹ Ñ‚Ð¾Ñ‡ÐºÐ¸, Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚ Ð¿ÐµÑ€ÐµÐ²Ð¾Ñ€Ð°Ñ‡Ð¸Ð²Ð°ÐµÐ¼
		var tmp = [];
		n = e;
		while (n !== ep) {
			var _n = n;
			while ((_n = this.rte.dom.prev(_n))) {
				tmp.push(_n);
			}
			n = n.parentNode;
		}
		if (tmp.length) {
			res.nodes = res.nodes.concat(tmp.reverse());
		}
		//  ÐµÑÐ»Ð¸ ÐºÐ¾Ð½ÐµÑ‡Ð½Ð°Ñ Ð½Ð¾Ð´Ð° Ð½Ðµ Ð¿ÑƒÑÑ‚Ð°Ñ Ð¸ != Ð½Ð°Ñ‡Ð°Ð»ÑŒÐ½Ð¾Ð¹ - Ð´Ð¾Ð±Ð°Ð²Ð»ÑÐµÐ¼ ÐµÐµ
		if (!isEmptySelected(e, 0, e.nodeType==3 ? res.eo : null)) {
			res.nodes.push(e);
		}
		
		if (blocks) {
			// Ð´Ð¾Ð±Ð°Ð²Ð»ÑÐµÐ¼ Ð¸Ð½Ð»Ð°Ð¹Ð½ ÑÐ¾ÑÐµÐ´ÐµÐ¹ ÑÐ»ÐµÐ²Ð°
			if (this.rte.dom.isInline(s)) {
				res.nodes = this.rte.dom.toLineStart(s).concat(res.nodes);
				res.so    = res.nodes[0].nodeType == 1 ? null : 0;
			}
			// Ð´Ð¾Ð±Ð°Ð²Ð»ÑÐµÐ¼ Ð¸Ð½Ð»Ð°Ð¹Ð½ ÑÐ¾ÑÐµÐ´ÐµÐ¹ ÑÐ¿Ñ€Ð°Ð²Ð°
			if (this.rte.dom.isInline(e)) {
				res.nodes = res.nodes.concat(this.rte.dom.toLineEnd(e));
				res.eo    = res.nodes[res.nodes.length-1].nodeType == 1 ? null : res.nodes[res.nodes.length-1].nodeValue.length;
			}
		}
		
		// Ð²ÑÐµ Ñ€Ð°Ð´ÑƒÑŽÑ‚ÑÑ! :)
		return res;
	}
	
}

/**
 * @class elRTE User interface controller
 *
 * @param  elRTE  rte Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui = function(rte) {
	var self      = this;
	this.rte      = rte;
	this._buttons = [];
	
	for (var i in this.buttons) {
		if (i != 'button') {
			this.buttons[i].prototype = this.buttons.button.prototype;
		}
	}
	
	// ÑÐ¾Ð·Ð´Ð°ÐµÐ¼ Ð¿Ð°Ð½ÐµÐ»Ð¸ Ð¸ ÐºÐ½Ð¾Ð¿ÐºÐ¸
	var toolbar = rte.options.toolbar && rte.options.toolbars[rte.options.toolbar] ? rte.options.toolbar : 'normal';
	var panels  = this.rte.options.toolbars[toolbar];
	for (var i in panels) {
		var name = panels[i];
		
		var panel = $('<ul />').addClass('panel-'.name).appendTo(this.rte.toolbar);
		if (i == 0) {
			panel.addClass('first');
		}
		for (var j in this.rte.options.panels[name]) {
			var n = this.rte.options.panels[name][j];
			var c = this.buttons[n] || this.buttons.button; 
			var b = new c(this.rte, n);
			panel.append(b.domElem);
			this._buttons.push(b);
		}
	}

	/**
	 * ÐŸÐµÑ€ÐµÐºÐ»ÑŽÑ‡Ð°ÐµÑ‚ Ð²Ð¸Ð´ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€Ð° Ð¼ÐµÐ¶Ð´Ñƒ Ð¾ÐºÐ½Ð¾Ð¼ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸ Ð¸ÑÑ…Ð¾Ð´Ð½Ð¸ÐºÐ°
	 **/
	this.rte.tabsbar.children('.tab').click(function(e) {
		if (!$(e.currentTarget).hasClass('active')) {
			self.rte.tabsbar.children('.tab').toggleClass('active');
			self.rte.workzone.children().toggle();
			if ($(e.currentTarget).hasClass('editor')) {
				self.rte.updateEditor();
			} else {
				self.rte.updateSource();
				$.each(self._buttons, function() {
					!this.active && this.domElem.addClass('disabled');
				});
				self.rte.source.focus();
			}
			
		}
	});

	this.update();
}

/**
 * ÐžÐ±Ð½Ð¾Ð²Ð»ÑÐµÑ‚ ÐºÐ½Ð¾Ð¿ÐºÐ¸ - Ð²Ñ‹Ð·Ñ‹Ð²Ð°ÐµÑ‚ Ð¼ÐµÑ‚Ð¾Ð´ update() Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ ÐºÐ½Ð¾Ð¿ÐºÐ¸
 *
 * @return void
 **/
elRTE.prototype.ui.prototype.update = function(cleanCache) {
	cleanCache && this.rte.selection.cleanCache();
	var n    = this.rte.selection.getNode();
	var p    = this.rte.dom.parents(n, '*');
	var path = '';
	if (p.length) {
		$.each(p.reverse(), function() {
			path += ' &raquo; '+ this.nodeName.toLowerCase();
		});
	}
	if (n.nodeType == 1 && n.nodeName != 'BODY') {
		path += ' &raquo; '+ n.nodeName.toLowerCase();
	}
	this.rte.statusbar.html(path)
	$.each(this._buttons, function() {
		this.update();
	});
	this.rte.window.focus();
}



elRTE.prototype.ui.prototype.buttons = {
	
	/**
	 * @class ÐºÐ½Ð¾Ð¿ÐºÐ° Ð½Ð° toolbar Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€Ð° 
	 * Ñ€ÐµÐ°Ð»Ð¸Ð·ÑƒÐµÑ‚ Ð¿Ð¾Ð²ÐµÐ´ÐµÐ½Ð¸Ðµ Ð¿Ð¾ ÑƒÐ¼Ð¾Ð»Ñ‡Ð°Ð½Ð¸ÑŽ Ð¸ ÑÐ²Ð»ÑÐµÑ‚ÑÑ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÐµÐ¹ Ð´Ð»Ñ Ð´Ñ€ÑƒÐ³Ð¸Ñ… ÐºÐ½Ð¾Ð¿Ð¾Ðº
	 *
	 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
	 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ (ÐºÐ¾Ð¼Ð°Ð½Ð´Ð° Ð¸ÑÐ¿Ð¾Ð»Ð½ÑÐµÐ¼Ð°Ñ document.execCommand())
	 **/
	button : function(rte, name) {
		var self     = this;
		this.rte     = rte;
		this.active = false;
		this.name    = name;
		this.val     = null;
		this.domElem = $('<li />')
			.addClass(name+' rounded-3')
			.attr({name : name, title : this.rte.i18n(this.rte.options.buttons[name] || name), unselectable : 'on'})
			.hover(
				function() { $(this).addClass('hover'); },
				function() { $(this).removeClass('hover'); }
			)
			.click( function(e) {
				e.stopPropagation();
				e.preventDefault();
				if (!$(this).hasClass('disabled')) {
					self.command();
				}
			});
	}
}

/**
 * ÐžÐ±Ñ€Ð°Ð±Ð¾Ñ‚Ñ‡Ð¸Ðº Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ Ð½Ð° ÐºÐ½Ð¾Ð¿ÐºÑƒ Ð½Ð° Ñ‚ÑƒÐ»Ð±Ð°Ñ€Ðµ. Ð’Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ðµ ÐºÐ¾Ð¼Ð°Ð½Ð´Ñ‹ Ð¸Ð»Ð¸ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ð¸Ðµ Ð¾ÐºÐ½Ð°|Ð¼ÐµÐ½ÑŽ Ð¸ Ñ‚Ð´
 *
 * @return void
 **/
elRTE.prototype.ui.prototype.buttons.button.prototype.command = function() {
	try {
		this.rte.doc.execCommand(this.name, false, this.val);
	} catch(e) {
		this.rte.log('commands failed: '+this.name);
	}
	this.rte.ui.update(true);
}

/**
 * ÐžÐ±Ð½Ð¾Ð²Ð»ÑÐµÑ‚ ÑÐ¾ÑÑ‚Ð¾ÑÐ½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸
 *
 * @return void
 **/
elRTE.prototype.ui.prototype.buttons.button.prototype.update = function() {
	try {
		if (!this.rte.doc.queryCommandEnabled(this.name)) {
			return this.domElem.addClass('disabled');
		} else {
			this.domElem.removeClass('disabled');
		}
	} catch (e) {
		return;
	}
	try {
		if (this.rte.doc.queryCommandState(this.name)) {
			this.domElem.addClass('active');
		} else {
			this.domElem.removeClass('active');
		}
	} catch (e) { }
}

/*
 * Misc utils for elRTE
 *
 * @param Object rte - editor
 * @todo ÐŸÐ¾Ð´ÑƒÐ¼Ð°Ñ‚ÑŒ, Ñ‡Ñ‚Ð¾ Ð¸Ð· ÑÑ‚Ð¾Ð³Ð¾ Ñ€ÐµÐ°Ð»ÑŒÐ½Ð¾ Ð½ÑƒÐ¶Ð½Ð¾ Ð¸ Ð½Ð°Ð²ÐµÑÑ‚Ð¸ Ð¿Ð¾Ñ€ÑÐ´Ð¾Ðº. Ð’Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾ Ñ‡Ð°ÑÑ‚ÑŒ Ð¿ÐµÑ€ÐµÐ½ÐµÑÑ‚Ð¸ Ð² ellib
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 */
elRTE.prototype.utils = function(rte) {
	this.rte     = rte;
	this.url     = null;
	// domo arigato, Steave, http://blog.stevenlevithan.com/archives/parseuri
	this.reg     = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
	this.baseURL = '';
	this.path    = '';
	var self     = this;
	
	this.rgb2hex = function(str) {
	    function hex(x)  {
	    	hexDigits = ["0", "1", "2", "3", "4", "5", "6", "7", "8","9", "a", "b", "c", "d", "e", "f"];
	        return !x  ? "00" : hexDigits[(x - x % 16) / 16] + hexDigits[x% 16];
	    }
		var rgb = str.match(/\(([0-9]{1,3}),\s*([0-9]{1,3}),\s*([0-9]{1,3})\)/); 
		return rgb ? "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]) : '';
	}
	
	this.toPixels = function(num) {
		var m = num.match(/([0-9]+\.?[0-9]*)\s*(px|pt|em|%)/);
		if (m) {
			num  = m[1];
			unit = m[2];
		} 
		if (num[0] == '.') {
			num = '0'+num;
		}
		num = parseFloat(num);

		if (isNaN(num)) {
			return '';
		}
		var base = parseInt($(document.body).css('font-size')) || 16;
		switch (unit) {
			case 'em': return parseInt(num*base);
			case 'pt': return parseInt(num*base/12);
			case '%' : return parseInt(num*base/100);
		}
		return num;
	}
	
	// TODO: add parse rel path ../../etc
	this.absoluteURL = function(url) {
		!this.url && this._url();
		url = $.trim(url);
		if (!url) {
			return '';
		}
		// ÑÑÑ‹Ð»ÐºÐ¸ Ð½Ð° ÑÐºÐ¾Ñ€Ñ Ð½Ðµ Ð¿ÐµÑ€ÐµÐ²Ð¾Ð´Ð¸Ð¼ Ð² Ð°Ð±Ñ
		if (url[0] == '#') {
			return url;
		}
		var u = this.parseURL(url);

		if (!u.host && !u.path && !u.anchor) {
			//this.rte.log('Invalid URL: '+url)
			return '';
		}
		if (!this.rte.options.absoluteURLs) { 
			return url;
		}
		if (u.protocol) {
			//this.rte.log('url already absolute: '+url);
			return url;
		}
		if (u.host && (u.host.indexOf('.')!=-1 || u.host == 'localhost')) {
			//this.rte.log('no protocol');
			return this.url.protocol+'://'+url;
		}
		if (url[0] == '/') {
			url = this.baseURL+url;
		} else {
			if (url.indexOf('./') == 0) {
				url = url.substring(2);
			}
			url = this.baseURL+this.path+url;
		}
		return url;
	}
	
	this.parseURL = function(url) {
		var u   = url.match(this.reg);
		var ret = {};
		$.each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(i) {
			ret[this] = u[i];
		});
		if (!ret.host.match(/[a-z0-9]/i)) {
			ret.host = '';
		}
		return ret;
	}
	
	this.trimEventCallback = function(c) {
		c = c ? c.toString() : '';
		return $.trim(c.replace(/\r*\n/mg, '').replace(/^function\s*on[a-z]+\s*\(\s*event\s*\)\s*\{(.+)\}$/igm, '$1'));
	}
	
	this._url = function() {
		this.url     = this.parseURL(window.location.href);
		this.baseURL = this.url.protocol+'://'+(this.url.userInfo ?  parts.userInfo+'@' : '')+this.url.host+(this.url.port ? ':'+this.url.port : '');
		this.path    = !this.url.file ? this.url.path : this.url.path.substring(0, this.url.path.length - this.url.file.length);
	}
	
}

/**
 * @class w3cRange  - w3c text range emulation for "strange" browsers
 *
 * @param  elRTE  rte  Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.w3cRange = function(rte) {
	var self                     = this;
	this.rte                     = rte;
	this.r                       = null;
	this.collapsed               = true;
	this.startContainer          = null;
	this.endContainer            = null;
	this.startOffset             = 0;
	this.endOffset               = 0;
	this.commonAncestorContainer = null;
	
	this.range = function() {
		try { 
			this.r = this.rte.window.document.selection.createRange(); 
		} catch(e) { 
			this.r = this.rte.doc.body.createTextRange(); 
		}
		return this.r;
	}
	
	this.insertNode = function(html) {
		this.range();
		self.r.collapse(false)
		var r = self.r.duplicate();
		r.pasteHTML(html);
	}
	
	this.getBookmark = function() {
		this.range();
		if (this.r.item) {
			var n = this.r.item(0);
			this.r = this.rte.doc.body.createTextRange();
			this.r.moveToElementText(n);
		}
		return this.r.getBookmark();
	}
	
	this.moveToBookmark = function(bm) {
		this.rte.window.focus();
		this.range().moveToBookmark(bm);
		this.r.select();
	}
	
	/**
	 * ÐžÐ±Ð½Ð¾Ð²Ð»ÑÐµÑ‚ Ð´Ð°Ð½Ð½Ñ‹Ðµ Ð¾ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð½Ñ‹Ñ… Ð½Ð¾Ð´Ð°Ñ…
	 *
	 * @return void
	 **/
	this.update = function() {

		function _findPos(start) {
			var marker = '\uFEFF';
			var ndx = offset = 0;
			var r = self.r.duplicate();
			r.collapse(start);
			var p = r.parentElement();
			if (!p || p.nodeName == 'HTML') {
				return {parent : self.rte.doc.body, ndx : ndx, offset : offset};
			}

			r.pasteHTML(marker);
			
			childs = p.childNodes;
			for (var i=0; i < childs.length; i++) {
				var n = childs[i];
				if (i>0 && (n.nodeType!==3 || childs[i-1].nodeType !==3)) {
					ndx++;
				}
				if (n.nodeType !== 3) {
					offset = 0;
				} else {
					var pos = n.nodeValue.indexOf(marker);
					if (pos !== -1) {
						offset += pos;
						break;
					}
					offset += n.nodeValue.length;
				}
			};
			r.moveStart('character', -1);
			r.text = '';
			return {parent : p, ndx : Math.min(ndx, p.childNodes.length-1), offset : offset};
		}

		this.range();
		this.startContainer = this.endContainer = null;

		if (this.r.item) {
			this.collapsed = false;
			var i = this.r.item(0);
			this.setStart(i.parentNode, this.rte.dom.indexOf(i));
			this.setEnd(i.parentNode, this.startOffset+1);
		} else {
			this.collapsed = this.r.boundingWidth == 0;
			var start = _findPos(true); 
			var end   = _findPos(false);
			
			start.parent.normalize();
			end.parent.normalize();
			start.ndx = Math.min(start.ndx, start.parent.childNodes.length-1);
			end.ndx = Math.min(end.ndx, end.parent.childNodes.length-1);
			if (start.parent.childNodes[start.ndx].nodeType && start.parent.childNodes[start.ndx].nodeType == 1) {
				this.setStart(start.parent, start.ndx);
			} else {
				this.setStart(start.parent.childNodes[start.ndx], start.offset);
			}
			if (end.parent.childNodes[end.ndx].nodeType && end.parent.childNodes[end.ndx].nodeType == 1) {
				this.setEnd(end.parent, end.ndx);
			} else {
				this.setEnd(end.parent.childNodes[end.ndx], end.offset);
			}
			// this.dump();
			this.select();
		}
		return this;
	}
	
	this.isCollapsed = function() {
		this.range();
		this.collapsed = this.r.item ? false : this.r.boundingWidth == 0;
		return this.collapsed;
	}
	
	/**
	 * "Ð¡Ñ…Ð»Ð¾Ð¿Ñ‹Ð²Ð°ÐµÑ‚" Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ
	 *
	 * @param  bool  toStart - ÑÑ…Ð»Ð¾Ð¿Ñ‹Ð²Ð°Ñ‚ÑŒ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ Ðº Ð½Ð°Ñ‡Ð°Ð»Ñƒ Ð¸Ð»Ð¸ Ðº ÐºÐ¾Ð½Ñ†Ñƒ
	 * @return void
	 **/
	this.collapse = function(toStart) {
		this.range();
		if (this.r.item) {
			var n = this.r.item(0);
			this.r = this.rte.doc.body.createTextRange();
			this.r.moveToElementText(n);
		}
		this.r.collapse(toStart);
		this.r.select();
		this.collapsed = true;
	}

	this.getStart = function() {
		this.range();
		if (this.r.item) {
			return this.r.item(0);
		}
		var r = this.r.duplicate();
		r.collapse(true);
		var s = r.parentElement();
		return s && s.nodeName == 'BODY' ? s.firstChild : s;
	}
	
	
	this.getEnd = function() {
		this.range();
		if (this.r.item) {
			return this.r.item(0);
		}
		var r = this.r.duplicate();
		r.collapse(false);
		var e = r.parentElement();
		return e && e.nodeName == 'BODY' ? e.lastChild : e;
	}

	
	/**
	 * Ð£ÑÑ‚Ð°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ‚ Ð½Ð°Ñ‡aÐ»Ð¾ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð½Ð° ÑƒÐºÐ°Ð·Ð°Ð½ÑƒÑŽ Ð½Ð¾Ð´Ñƒ
	 *
	 * @param  Element  node    Ð½Ð¾Ð´Ð°
	 * @param  Number   offset  Ð¾Ñ‚ÑÑ‚ÑƒÐ¿ Ð¾Ñ‚ Ð½Ð°Ñ‡Ð°Ð»Ð° Ð½Ð¾Ð´Ñ‹
	 * @return void
	 **/
	this.setStart = function(node, offset) {
		this.startContainer = node;
		this.startOffset    = offset;
		if (this.endContainer) {
			this.commonAncestorContainer = this.rte.dom.findCommonAncestor(this.startContainer, this.endContainer);
		}
	}
	
	/**
	 * Ð£ÑÑ‚Ð°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ‚ ÐºÐ¾Ð½ÐµÑ† Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð½Ð° ÑƒÐºÐ°Ð·Ð°Ð½ÑƒÑŽ Ð½Ð¾Ð´Ñƒ
	 *
	 * @param  Element  node    Ð½Ð¾Ð´Ð°
	 * @param  Number   offset  Ð¾Ñ‚ÑÑ‚ÑƒÐ¿ Ð¾Ñ‚ ÐºÐ¾Ð½Ñ†Ð° Ð½Ð¾Ð´Ñ‹
	 * @return void
	 **/
	this.setEnd = function(node, offset) {
		this.endContainer = node;
		this.endOffset    = offset;
		if (this.startContainer) {
			this.commonAncestorContainer = this.rte.dom.findCommonAncestor(this.startContainer, this.endContainer);
		}
	}
	
	/**
	 * Ð£ÑÑ‚Ð°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ‚ Ð½Ð°Ñ‡aÐ»Ð¾ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´ ÑƒÐºÐ°Ð·Ð°Ð½Ð¾Ð¹ Ð½Ð¾Ð´Ð¾Ð¹
	 *
	 * @param  Element  node    Ð½Ð¾Ð´Ð°
	 * @return void
	 **/
	this.setStartBefore = function(n) {
		if (n.parentNode) {
			this.setStart(n.parentNode, this.rte.dom.indexOf(n));
		}
	}
	
	/**
	 * Ð£ÑÑ‚Ð°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ‚ Ð½Ð°Ñ‡aÐ»Ð¾ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¿Ð¾ÑÐ»Ðµ ÑƒÐºÐ°Ð·Ð°Ð½Ð¾Ð¹ Ð½Ð¾Ð´Ñ‹
	 *
	 * @param  Element  node    Ð½Ð¾Ð´Ð°
	 * @return void
	 **/
	this.setStartAfter = function(n) {
		if (n.parentNode) {
			this.setStart(n.parentNode, this.rte.dom.indexOf(n)+1);
		}
	}
	
	/**
	 * Ð£ÑÑ‚Ð°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ‚ ÐºÐ¾Ð½ÐµÑ† Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´ ÑƒÐºÐ°Ð·Ð°Ð½Ð¾Ð¹ Ð½Ð¾Ð´Ð¾Ð¹
	 *
	 * @param  Element  node    Ð½Ð¾Ð´Ð°
	 * @return void
	 **/
	this.setEndBefore = function(n) {
		if (n.parentNode) {
			this.setEnd(n.parentNode, this.rte.dom.indexOf(n));
		}
	}
	
	/**
	 * Ð£ÑÑ‚Ð°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ‚ ÐºÐ¾Ð½ÐµÑ† Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¿Ð¾ÑÐ»Ðµ ÑƒÐºÐ°Ð·Ð°Ð½Ð¾Ð¹ Ð½Ð¾Ð´Ñ‹
	 *
	 * @param  Element  node    Ð½Ð¾Ð´Ð°
	 * @return void
	 **/
	this.setEndAfter = function(n) {
		if (n.parentNode) {
			this.setEnd(n.parentNode, this.rte.dom.indexOf(n)+1);
		}
	}
	
	/**
	 * Ð£ÑÑ‚Ð°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ‚ Ð½Ð¾Ð²Ð¾Ðµ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ Ð¿Ð¾ÑÐ»Ðµ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹
	 *
	 * @return void
	 **/
	this.select = function() {
		// thanks tinymice authors
		function getPos(n, o) {
			if (n.nodeType != 3) {
				return -1;
			}
			var c   ='\uFEFF';
			var val = n.nodeValue;
			var r   = self.rte.doc.body.createTextRange();
			n.nodeValue = val.substring(0, o) + c + val.substring(o);
			r.moveToElementText(n.parentNode);
			r.findText(c);
			var p = Math.abs(r.moveStart('character', -0xFFFFF));
			n.nodeValue = val;
			return p;
		};
		
		this.r = this.rte.doc.body.createTextRange(); 
		var so = this.startOffset;
		var eo = this.endOffset;
		var s = this.startContainer.nodeType == 1 
			? this.startContainer.childNodes[Math.min(so, this.startContainer.childNodes.length - 1)]
			: this.startContainer;
		var e = this.endContainer.nodeType == 1 
			? this.endContainer.childNodes[Math.min(so == eo ? eo : eo - 1, this.endContainer.childNodes.length - 1)]
			: this.endContainer;

		if (this.collapsed) {
			if (s.nodeType == 3) {
				var p = getPos(s, so);
				this.r.move('character', p);
			} else {
				this.r.moveToElementText(s);
				this.r.collapse(true);
			}
		} else {
			var r  = this.rte.doc.body.createTextRange(); 
			var sp = getPos(s, so);
			var ep = getPos(e, eo);
			if (s.nodeType == 3) {
				this.r.move('character', sp);
			} else {
				this.r.moveToElementText(s);
			}
			if (e.nodeType == 3) {
				r.move('character', ep);
			} else {
				r.moveToElementText(e);
			}
			this.r.setEndPoint('EndToEnd', r);
		}
		
		try {
			this.r.select();
		} catch(e) {
			
		}
		if (r) {
			r = null;
		}
	}
	
	this.dump = function() {
		this.rte.log('collapsed: '+this.collapsed);
		//this.rte.log('commonAncestorContainer: '+this.commonAncestorContainer.nodeName||'#text')
		this.rte.log('startContainer: '+(this.startContainer ? this.startContainer.nodeName : 'non'));
		this.rte.log('startOffset: '+this.startOffset);
		this.rte.log('endContainer: '+(this.endContainer ? this.endContainer.nodeName : 'none'));
		this.rte.log('endOffset: '+this.endOffset);
	}
	
}

/**
 * @class ÐºÐ½Ð¾Ð¿ÐºÐ° - Ð—Ð°ÐºÐ»Ð°Ð´ÐºÐ° (Ð¾Ñ‚ÐºÑ€Ñ‹Ð²Ð°ÐµÑ‚ Ð´Ð¸Ð°Ð»Ð¾Ð³Ð¾Ð²Ð¾Ðµ Ð¾ÐºÐ½Ð¾)
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.anchor = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	this.input = $('<input type="text" />').attr('name', 'anchor').attr('size', '16')
	var self = this;
	
	this.command = function() {
		var opts = {
			submit : function(e, d) { e.stopPropagation(); e.preventDefault(); d.close(); self.set();  },
			dialog : {
				title : this.rte.i18n('Bookmark')
			}
		}

		this.anchor = this.rte.dom.selfOrParentAnchor(this.rte.selection.getEnd()) || rte.dom.create('a');
		!this.rte.selection.collapsed() && this.rte.selection.collapse(false);
		this.input.val($(this.anchor).addClass('el-rte-anchor').attr('name'));
		this.rte.selection.saveIERange();
		var d = new elDialogForm(opts);
		d.append([this.rte.i18n('Bookmark name'), this.input], null, true).open();
	}
	
	this.update = function() {
		var n = this.rte.selection.getNode();
		if (this.rte.dom.selfOrParentLink(n)) {
			this.domElem.addClass('disabled');
		} else if (this.rte.dom.selfOrParentAnchor(n)) {
			this.domElem.removeClass('disabled').addClass('active');
		} else {
			this.domElem.removeClass('disabled').removeClass('active');
		}
	}
	
	this.set = function() {
		var n = $.trim(this.input.val());
		this.rte.selection.restoreIERange();
		this.rte.log(this.anchor.parentNode)
		if (n) {
			if (!this.anchor.parentNode) {
				this.rte.selection.insertHtml('<a name="'+n+'" title="'+this.rte.i18n('Bookmark')+': '+n+'" class="el-rte-anchor"></a>');
			} else {
				this.anchor.name = n;
				this.anchor.title = this.rte.i18n('Bookmark')+': '+n;
			}
		} else if (this.anchor.parentNode) {
			this.anchor.parentNode.removeChild(this.anchor);
		}
	}
}

/**
 * @class ÐºÐ½Ð¾Ð¿ÐºÐ° - Ð¦Ð¸Ñ‚Ð°Ñ‚Ð°
 * Ð•ÑÐ»Ð¸ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ ÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚Ð¾ Ð¸ Ð½Ð°Ñ…Ð¾Ð´Ð¸Ñ‚ÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ Ñ†Ð¸Ñ‚Ð°Ñ‚Ñ‹ - Ð¾Ð½Ð° ÑƒÐ´Ð°Ð»ÑÐµÑ‚ÑÑ
 * ÐÐ¾Ð²Ñ‹Ðµ Ñ†Ð¸Ñ‚Ð°Ñ‚Ñ‹ ÑÐ¾Ð·Ð´Ð°ÑŽÑ‚ÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð¸Ð· Ð½ÐµÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚Ð¾Ð³Ð¾ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.blockquote = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		var n, nodes;
		if (this.rte.selection.collapsed() && (n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^BLOCKQUOTE$/))) {
			$(n).replaceWith($(n).html());
		} else {
			nodes = this.rte.selection.selected({wrap : 'all', tag : 'blockquote'});
			nodes.length && this.rte.selection.select(nodes[0], nodes[nodes.length-1]);
		}
		this.rte.ui.update(true);
	}
	
	this.update = function() {
		if (this.rte.selection.collapsed()) {
			if (this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^BLOCKQUOTE$/)) {
				this.domElem.removeClass('disabled').addClass('active');
			} else {
				this.domElem.addClass('disabled').removeClass('active');
			}
		} else {
			this.domElem.removeClass('disabled').removeClass('active');
		}
	}
}
/**
 * @class ÐºÐ½Ð¾Ð¿ÐºÐ¸ "ÐºÐ¾Ð¿Ð¸Ñ€Ð¾Ð²Ð°Ñ‚ÑŒ/Ð²Ñ‹Ñ€ÐµÐ·Ð°Ñ‚ÑŒ/Ð²ÑÑ‚Ð°Ð²Ð¸Ñ‚ÑŒ" 
 * Ð² firefox Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°ÐµÑ‚ Ð¿Ñ€ÐµÐ´Ð»Ð¾Ð¶ÐµÐ½Ð¸Ðµ Ð½Ð°Ð¶Ð°Ñ‚ÑŒ Ctl+c, Ð² Ð¾ÑÑ‚Ð°Ð»ÑŒÐ½Ñ‹Ñ… - ÐºÐ¾Ð¿Ð¸Ñ€ÑƒÐµÑ‚
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.copy = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		
		if (this.rte.browser.mozilla) {
			try {
				this.rte.doc.execCommand(this.name, false, null);
			} catch (e) {
				var s = ' Ctl + C';
				if (this.name == 'cut') {
					s = ' Ctl + X';
				} else if (this.name == 'paste') {
					s = ' Ctl + V';
				}
				var opts = {
					dialog : {
						title   : this.rte.i18n('Warning'),
						buttons : { Ok : function() { $(this).dialog('close'); } }
					}
				}

				var d = new elDialogForm(opts);
				d.append(this.rte.i18n('This operation is disabled in your browser on security reason. Use shortcut instead.')+': '+s).open();
			}
		} else {
			this.constructor.prototype.command.call(this);
		}
	}
}

elRTE.prototype.ui.prototype.buttons.cut   = elRTE.prototype.ui.prototype.buttons.copy;
elRTE.prototype.ui.prototype.buttons.paste = elRTE.prototype.ui.prototype.buttons.copy;
/**
 * @class ÐºÐ½Ð¾Ð¿ÐºÐ° - DIV
 * Ð•ÑÐ»Ð¸ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ ÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚Ð¾ Ð¸ Ð½Ð°Ñ…Ð¾Ð´Ð¸Ñ‚ÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ div'a - Ð¾Ð½ ÑƒÐ´Ð°Ð»ÑÐµÑ‚ÑÑ
 * ÐÐ¾Ð²Ñ‹Ðµ div'Ñ‹ ÑÐ¾Ð·Ð´Ð°ÑŽÑ‚ÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð¸Ð· Ð½ÐµÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚Ð¾Ð³Ð¾ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 * 
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.div = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		var n, nodes;
		if (this.rte.selection.collapsed() && (n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^DIV$/))) {
			$(n).replaceWith($(n).html());
		} else {
			nodes = this.rte.selection.selected({wrap : 'all', tag : 'div'});
			nodes.length && this.rte.selection.select(nodes[0], nodes[nodes.length-1]);
		}
		this.rte.ui.update(true);
	}
	
	this.update = function() {
		if (this.rte.selection.collapsed()) {
			if (this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^DIV$/)) {
				this.domElem.removeClass('disabled').addClass('active');
			} else {
				this.domElem.addClass('disabled').removeClass('active');
			}
		} else {
			this.domElem.removeClass('disabled').removeClass('active');
		}
	}
}

/**
 * @class ÐºÐ½Ð¾Ð¿ÐºÐ° - Ð’ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ðµ/Ð²Ñ‹ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ðµ Ð¿Ð¾ÐºÐ°Ð·Ð° ÑÑ‚Ñ€ÑƒÐºÑ‚ÑƒÑ€Ñ‹ Ð´Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 * 
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.docstructure = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		this.domElem.toggleClass('active');
		$(this.rte.doc.body).toggleClass('el-rte-structure');
	}
	this.command();
	
	this.update = function() {	
		this.domElem.removeClass('disabled');
	}
}

/**
 * @class button - open elfinder window (not needed for image or link buttons).Used in ELDORADO.CMS for easy file manipulations.
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.elfinder = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	this.command = function() {
		if (self.rte.options.fmAllow && typeof(self.rte.options.fmOpen) == 'function') {
			self.rte.options.fmOpen( function(url) { self.rte.log(url) } );
		}
	}
	
	this.update = function() {
		if (self.rte.options.fmAllow && typeof(self.rte.options.fmOpen) == 'function') {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

/**
 * @class drop-down menu - font-family for selected text
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.fontname = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	var opts = {
		tpl      : '<span style="font-family:%val">%label</span>',
		select   : function(v) { self.set(v); },
		src      : {
			''                                              : this.rte.i18n('Font'),
			'andale mono,sans-serif'                        : 'Andale Mono',
			'arial,helvetica,sans-serif'                    : 'Arial',
			'arial black,gadget,sans-serif'                 : 'Arial Black',
			'book antiqua,palatino,sans-serif'              : 'Book Antiqua',
			'comic sans ms,cursive'                         : 'Comic Sans MS',
			'courier new,courier,monospace'                 : 'Courier New',
			'georgia,palatino,serif'                        : 'Georgia',
			'helvetica,sans-serif'                          : 'Helvetica',
			'impact,sans-serif'                             : 'Impact',
			'lucida console,monaco,monospace'               : 'Lucida console',
			'lucida sans unicode,lucida grande,sans-serif'  : 'Lucida grande',
			'tahoma,sans-serif'                             : 'Tahoma',
			'times new roman,times,serif'                   : 'Times New Roman',
			'trebuchet ms,lucida grande,verdana,sans-serif' : 'Trebuchet MS',
			'verdana,geneva,sans-serif'                     : 'Verdana'
		}
	}
	
	this.select = this.domElem.elSelect(opts);
	
	this.command = function() {
	}
	
	this.set = function(size) {
		var nodes = this.rte.selection.selected({filter : 'textContainsNodes'});
		$.each(nodes, function() {
			$this = /^(THEAD|TFOOT|TBODY|COL|COLGROUP|TR)$/.test(this.nodeName) ? $(this).find('td,th') : $(this);
			$(this).css('font-family', size).find('[style]').css('font-family', '');
		});
		this.rte.ui.update();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled'); 
		var n = this.rte.selection.getNode();
		if (n.nodeType != 1) {
			n = n.parentNode;
		}
		var v = $(n).css('font-family');
		v = v ? v.toString().toLowerCase().replace(/,\s+/g, ',').replace(/'|"/g, '') : '';
		this.select.val(opts.src[v] ? v : '');
	}
}

/**
 * @class drop-down menu - font size for selected text
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.fontsize = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	var opts = {
		labelTpl : '%label',
		tpl      : '<span style="font-size:%val;line-height:1.2em">%label</span>',
		select   : function(v) { self.set(v); },
		src      : {
			''         : this.rte.i18n('Font size'),
			'xx-small' : this.rte.i18n('Small (8pt)'), 
			'x-small'  : this.rte.i18n('Small (10px)'), 
			'small'    : this.rte.i18n('Small (12pt)'), 
			'medium'   : this.rte.i18n('Normal (14pt)'),
			'large'    : this.rte.i18n('Large (18pt)'),
			'x-large'  : this.rte.i18n('Large (24pt)'),
			'xx-large' : this.rte.i18n('Large (36pt)')
		}
	}
	
	this.select = this.domElem.elSelect(opts);
	
	this.command = function() {
	}
	
	this.set = function(size) {
		var nodes = this.rte.selection.selected({filter : 'textContainsNodes'});
		$.each(nodes, function() {
			$this = /^(THEAD|TFOOT|TBODY|COL|COLGROUP|TR)$/.test(this.nodeName) ? $(this).find('td,th') : $(this);
			$this.css('font-size', size).find("[style]").css('font-size', '');
		});
		this.rte.ui.update();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		var n = this.rte.selection.getNode();
		this.select.val((m = this.rte.dom.attr(n, 'style').match(/font-size:\s*([^;]+)/i)) ? m[1] : '');
	}
}

/**
 * @class color pallete for text color and background
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.forecolor = function(rte, name) {
	var self = this;
	this.constructor.prototype.constructor.call(this, rte, name);
	var opts = {
		'class' : '',
		color   : this.defaultColor,
		update  : function(c) { self.indicator.css('background-color', c); },
		change  : function(c) { self.set(c) }
	}
	
	this.defaultColor = this.rte.utils.rgb2hex( $(this.rte.doc.body).css(this.name=='forecolor' ? 'color' : 'background-color') );
	this.picker       = this.domElem.elColorPicker(opts);
	this.indicator    = $('<div />').addClass('color-indicator').prependTo(this.domElem);
	
	this.command = function() {
	}
	
	this.set = function(c) {
		if (!this.rte.selection.collapsed()) {
			var nodes = this.rte.selection.selected({collapse : false, wrap : 'text'});
			var css   = this.name == 'forecolor' ? 'color' : 'background-color';			
			$.each(nodes, function() {
				if (/^(THEAD|TBODY|TFOOT|TR)$/.test(this.nodeName)) {
					$(this).find('td,th').each(function() {
						$(this).css(css, c).find('*').css(css, '');
					})
				} else {
					$(this).css(css, c).find('*').css(css, '');
				}
			});
			this.rte.ui.update(true);
		}
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		var n = this.rte.selection.getNode();
		if (n.nodeType != 1) {
			n = n.parentNode;
		}
		var v = $(n).css(this.name == 'forecolor' ? 'color' : 'background-color');
		this.picker.val(v && v!='transparent' ? this.rte.utils.rgb2hex(v): this.defaultColor);
	}
}

elRTE.prototype.ui.prototype.buttons.hilitecolor = elRTE.prototype.ui.prototype.buttons.forecolor;

/**
 * @class drop-down menu - formatting text block
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.formatblock = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);

	var cmd = this.rte.browser.msie 
		? function(v) { self.val = v; self.constructor.prototype.command.call(self); }
		: function(v) { self.ieCommand(v); } 
	var self = this;
	var opts = {
		labelTpl : '%label',
		tpls     : {'' : '%label'},
		select   : function(v) { self.formatBlock(v); },
		src      : {
			'span'    : this.rte.i18n('Format'),
			'h1'      : this.rte.i18n('Heading 1'),
			'h2'      : this.rte.i18n('Heading 2'),
			'h3'      : this.rte.i18n('Heading 3'),
			'h4'      : this.rte.i18n('Heading 4'),
			'h5'      : this.rte.i18n('Heading 5'),
			'h6'      : this.rte.i18n('Heading 6'),
			'p'       : this.rte.i18n('Paragraph'),
			'address' : this.rte.i18n('Address'),
			'pre'     : this.rte.i18n('Preformatted')
		}
	}

	this.select = this.domElem.elSelect(opts);
	
	this.command = function() {

	}
	
	this.formatBlock = function(v) {

		function format(n, tag) {
			
			function replaceChilds(p) {
				$(p).find('h1,h2,h3,h4,h5,h6,p,address,pre').each(function() {
					$(this).replaceWith($(this).html());
				});
			}
			
			if (/^(LI|DT|DD)$/.test(n.nodeName)) {
				replaceChilds(n);
				self.rte.dom.wrapContents(n, tag);
			} else if (/^(UL|OL|DL)$/.test(n.nodeName)) {
				var html = '';
				$(n).children().each(function() {
					replaceChilds(this);
					html += $(this).html();
				});
				$(n).replaceWith($(self.rte.dom.create(tag)).html(html||''));
				
			} else {
				replaceChilds(n);
				$(n).replaceWith( $(self.rte.dom.create(tag)).html($(n).html()));
			}
		}

		tag = v == 'span' ? '' : v.toUpperCase();
		var nodes = this.rte.selection.selected({
			collapsed : true,
			blocks    : true,
			wrap      : 'inline',
			tag       : 'span'
		});

		for (var i=0; i<nodes.length; i++) {
			var n = nodes[i];
			if (tag) {
				if (/^(TABLE|THEAD|TBODY|TFOOT|TR)$/.test(n.nodeName)) {
					$(n).find('td,th').each(function() { format(this, tag); });
				} else {
					format(n, tag);
				}
			} else {
				if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(n.nodeName)) {
					$(n).replaceWith($(this.rte.dom.create('div')).html($(n).html()||''));
				}
			}
		};
		this.rte.ui.update();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		var n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(H[1-6]|P|ADDRESS|PRE)$/);
		this.select.val(n ? n.nodeName.toLowerCase() : 'span');
	}
}

/**
 * @class button - switch to fullscreen mode and back
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.fullscreen = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	this.active  = true;
	this.parents = [];
	this.height  = 0;
	var self     = this;
	
	this.command = function() {
		
		if (this.rte.editor.hasClass('el-fullscreen')) {
			for (var i=0; i < this.parents.length; i++) {
				$(this.parents[i]).css('position', 'relative');
			};
			this.parents = [];
			this.rte.editor.removeClass('el-fullscreen');
			this.rte.workzone.height(this.height);
			this.domElem.removeClass('active');
		} else {
			this.parents = [];
			var p = this.rte.editor.parents().each(function() {
				
				if (this.nodeName != 'BODY' && this.name != 'HTML' && $(this).css('position') == 'relative') {
					self.parents.push(this);
					$(this).css('position', 'static');
				}
			});
			this.height = this.rte.workzone.height();
			this.rte.editor.addClass('el-fullscreen');
			var h = parseInt(this.rte.editor.height() - this.rte.toolbar.height() - this.rte.statusbar.height() - this.rte.tabsbar.height() - 17);
			h>0 && this.rte.workzone.height(h);
			this.domElem.addClass('active');
		}
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
	}
}

/**
 * @class button - horizontal rule (open dialog window)
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.horizontalrule = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	this.src = {
		width   : $('<input type="text" />').attr({'name' : 'width', 'size' : 4}).css('text-align', 'right'),
		wunit   : $('<select />').attr('name', 'wunit')
					.append($('<option />').val('%').text('%'))
					.append($('<option />').val('px').text('px'))
					.val('%'),
		height  : $('<input type="text" />').attr({'name' : 'height', 'size' : 4}).css('text-align', 'right'),
		bg      : $('<div />'),
		border  : $('<div />'),
		'class' : $('<input type="text" />').css('width', '100%'),
		style   : $('<input type="text" />').css('width', '100%')
	}
	
	this.command = function() {
		this.src.bg.elColorPicker({palettePosition : 'outer', 'class' : 'el-colorpicker ui-icon ui-icon-pencil'});
		
		var n   = this.rte.selection.getEnd();
		this.hr = n.nodeName == 'HR' ? $(n) : $(rte.doc.createElement('hr')).css({width : '100%', height : '1px'});
		this.src.border.elBorderSelect({styleHeight : 73, value : this.hr});
		
		var _w  = this.hr.css('width') || this.hr.attr('width');
		this.src.width.val(parseInt(_w) || 100);
		this.src.wunit.val(_w.indexOf('px') != -1 ? 'px' : '%');
		
		this.src.height.val( this.rte.utils.toPixels(this.hr.css('height') || this.hr.attr('height')) || 1) ;
		this.src.bg.val(this.rte.utils.rgb2hex(this.hr.css('background-color')) || '');
		this.src['class'].val(this.rte.dom.attr(this.hr, 'class'));
		this.src.style.val(this.rte.dom.attr(this.hr, 'style'));
		
		var opts = {
			submit : function(e, d) { e.stopPropagation(); e.preventDefault(); self.set(); d.close(); },
			dialog : {
				title : this.rte.i18n('Horizontal rule')
			}
		}

		var d = new elDialogForm(opts);
		d.append([this.rte.i18n('Width'),          $('<span />').append(this.src.width).append(this.src.wunit) ], null, true)
			.append([this.rte.i18n('Height'),      $('<span />').append(this.src.height).append(' px')], null, true)
			.append([this.rte.i18n('Border'),      this.src.border], null, true)
			.append([this.rte.i18n('Background'),  this.src.bg], null, true)
			.append([this.rte.i18n('Css class'),   this.src['class']], null, true)
			.append([this.rte.i18n('Css style'),   this.src.style], null, true)
			.open();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		if (this.rte.selection.getEnd().nodeName == 'HR') {
			this.domElem.addClass('active');
		} else {
			this.domElem.removeClass('active');
		}
	}
	
	this.set = function() {
		!this.hr.parentNode && this.rte.selection.insertNode(this.hr.get(0));
		var attr = {
			noshade : true,
			style   : this.src.style.val()
		}
		var b = this.src.border.val();
		var css = {
			width  : (parseInt(this.src.width.val()) || 100)+this.src.wunit.val(),
			height : parseInt(this.src.height.val()) || 1,
			'background-color' : this.src.bg.val(),
			border : b.width && b.style ? b.width+' '+b.style+' '+b.color : ''
		}

		this.hr.removeAttr('class')
			.removeAttr('style')
			.removeAttr('width')
			.removeAttr('height')
			.removeAttr('align')
			.attr(attr)
			.css(css);
		
		if (this.src['class'].val()) {
			this.hr.attr('class', this.src['class'].val());	
		}
	}
	
}

/**
 * @class button - insert/edit image (open dialog window)
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.image = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	this.img = null
	this.init = function() {
		
		this.labels = {
			main   : 'Properies',
			link   : 'Link',
			adv    : 'Advanced',
			events : 'Events',
			id        : 'ID',
			'class'   : 'Css class',
			style     : 'Css style',
			longdesc  : 'Detail description URL',
			href    : 'URL',
			target  : 'Open in',
			title   : 'Title'
		}
		
		this.src = {
			main : {
				src    : $('<input type="text" />').css('width', '100%'),
				title  : $('<input type="text" />').css('width', '100%'),
				alt    : $('<input type="text" />').css('width', '100%'),
				width  : $('<input type="text" />').attr('size', 5).css('text-align', 'right'),
				height : $('<input type="text" />').attr('size', 5).css('text-align', 'right'),
				margin : $('<div />'),
				align  : $('<select />').css('width', '100%')
							.append($('<option />').val('').text(this.rte.i18n('Not set', 'dialogs')))
							.append($('<option />').val('left'       ).text(this.rte.i18n('Left')))
							.append($('<option />').val('right'      ).text(this.rte.i18n('Right')))
							.append($('<option />').val('top'        ).text(this.rte.i18n('Top')))
							.append($('<option />').val('text-top'   ).text(this.rte.i18n('Text top')))
							.append($('<option />').val('middle'     ).text(this.rte.i18n('middle')))
							.append($('<option />').val('baseline'   ).text(this.rte.i18n('Baseline')))
							.append($('<option />').val('bottom'     ).text(this.rte.i18n('Bottom')))
							.append($('<option />').val('text-bottom').text(this.rte.i18n('Text bottom'))),
				border : $('<div />')
			},

			adv : {
				id       : $('<input type="text" />').css('width', '100%'),
				'class'  : $('<input type="text" />').css('width', '100%'),
				style    : $('<input type="text" />').css('width', '100%'),
				longdesc : $('<input type="text" />').css('width', '100%')
			},

			// link : {
			// 	href  : $('<input type="text" />').css('width', '100%'),
			// 	title : $('<input type="text" />').css('width', '100%')
			// },
			
			events : {}
		}
		
		$.each(
			['onblur', 'onfocus', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onmouseleave', 'onkeydown', 'onkeypress', 'onkeyup'], 
			function() {
				self.src.events[this] = $('<input type="text" />').css('width', '100%');
		});
		
		$.each(self.src, function() {
			for (var n in this) {
				this[n].attr('name', n);
			}
		});
		
	}
	
	this.command = function() {
		!this.src && this.init();
		this.rte.browser.msie && this.rte.selection.saveIERange();
		this.src.main.border.elBorderSelect({ change : function() { self.updateImg(); }, name : 'border' });
		this.src.main.margin.elPaddingInput({ type : 'margin' });

		this.cleanValues();
		this.src.main.src.val('');
		
		var n = this.rte.selection.getEnd();
		this.preview = null;
		this.prevImg = null;
		this.link    = null;
		if (n.nodeName == 'IMG') {
			this.img     = $(n);
		} else {
			this.img = $(this.rte.doc.createElement('img'));
			
		}
		
		var opts = {
			submit : function(e, d) { e.stopPropagation(); e.preventDefault(); self.set(); d.close(); },
			dialog : {
				width    : 520,
				position : 'top',
				title    : this.rte.i18n('Image')
			}
		}
		var d = new elDialogForm(opts);
		
		if (this.rte.options.fmAllow && this.rte.options.fmOpen) {
			var src = $('<span />').append(this.src.main.src.css('width', '88%'))
					.append(
						$('<span />').addClass('ui-state-default ui-corner-all')
							.css({'float' : 'right', 'margin-right' : '3px'})
							.attr('title', self.rte.i18n('Open file manger'))
							.append($('<span />').addClass('ui-icon ui-icon-folder-open'))
								.click( function() {
									self.rte.options.fmOpen( function(url) { self.src.main.src.val(url).change() } );
								})
								.hover(function() {$(this).addClass('ui-state-hover')}, function() { $(this).removeClass('ui-state-hover')})
						);
		} else {
			var src = this.src.main.src;
		}
		
		d.tab('main', this.rte.i18n('Properies'))
			.append([this.rte.i18n('Image URL'), src],                 'main', true)
			.append([this.rte.i18n('Title'),     this.src.main.title], 'main', true)
			.append([this.rte.i18n('Alt text'),  this.src.main.alt],   'main', true)
			.append([this.rte.i18n('Size'), $('<span />').append(this.src.main.width).append(' x ').append(this.src.main.height).append(' px')], 'main', true)
			.append([this.rte.i18n('Alignment'), this.src.main.align],  'main', true)
			.append([this.rte.i18n('Margins'),   this.src.main.margin], 'main', true)
			.append([this.rte.i18n('Border'),    this.src.main.border], 'main', true)

		for (var tab in this.src) {
			if (tab != 'main') {
				d.tab(tab, this.rte.i18n(this.labels[tab]));
				for (var name in this.src[tab]) {
					var l = this.rte.i18n(this.labels[name] ? this.labels[name] : name);
					if (tab == 'events') {
						this.src[tab][name].val(this.rte.utils.trimEventCallback(this.img.attr(name)));
					} else if (tab == 'link') {
						if (this.link) {
							this.src[tab][name].val(name == 'href' ? this.rte.utils.absoluteURL(this.link.attr(name)) : this.link.attr(name));
						}
					} else {
						this.src[tab][name].val(this.img.attr(name)||'');
					}
					d.append([l, this.src[tab][name]], tab, true);
				}
			}
		};
				
		d.open();
		
		var fs = $('<fieldset />').append($('<legend />').text(this.rte.i18n('Preview')))
		d.append(fs, 'main');
		var frame = document.createElement('iframe');
		$(frame).attr('src', '#').addClass('el-rte-preview').appendTo(fs);

		html = this.rte.options.doctype+'<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></head><body style="padding:0;margin:0;font-size:9px"> </body></html>';
		frame.contentWindow.document.open();
		frame.contentWindow.document.write(html);
		frame.contentWindow.document.close();
		this.frame = frame.contentWindow.document
		this.preview = $(frame.contentWindow.document.body)
		 				 .text('Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin');
		
		if (this.img.attr('src')) {
			
			this.prevImg = $(this.frame.createElement('img'))
				.attr('src',  this.rte.utils.absoluteURL(this.img.attr('src')))
				
			this.prevImg.attr('width', this.img.attr('width'))
				.attr('height', this.img.attr('height'))
				.attr('title', this.img.attr('title')||'')
				.attr('alt', this.img.attr('alt')||'')
				.attr('style', this.img.attr('style')||'')
			for (var n in this.src.adv) {
				var a = this.img.attr(n);
				if (a) {
					this.prevImg.attr(n, a)
				}
			}	
				
			this.preview.prepend(this.prevImg);
			this.updateValues();
		}
		
		$.each(this.src, function() {
			$.each(this, function() {
				if (this === self.src.main.src) {
					this.bind('change', function() { self.updatePreview(); });
				} else if (this == self.src.main.width || this == self.src.main.height) {
					this.bind('change', function(e) {self.updateDimesions(e);});
				} else {
					this.bind('change', function() { self.updateImg(); });
				}
			});
		});
		
		// this.src.link.href.change(function() {
		// 	var $this = $(this);
		// 	$this.val(self.rte.utils.absoluteURL($this.val()));
		// });
		
	}
	

	
	/**
	 * Ð£ÑÑ‚Ð°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ‚ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÐµÐ¹ Ñ„Ð¾Ñ€Ð¼Ñ‹ Ð¸Ð· Ð°Ñ‚Ñ‚Ñ€Ð¸Ð±ÑƒÑ‚Ð¾Ð² prevImg
	 * Ð’Ñ‹Ð·Ñ‹Ð²Ð°ÐµÑ‚ÑÑ Ð¿Ð¾ÑÐ»Ðµ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ prevImg
	 *
	 **/
	this.updateValues = function() {
		
		var i = this.prevImg.get(0);
		
		this.origW = this.prevImg.attr('width'); 
		this.origH = this.prevImg.attr('height');
		
		this.src.main.src.val(this.rte.dom.attr(i, 'src'));
		this.src.main.title.val(this.rte.dom.attr(i, 'title'));		
		this.src.main.alt.val(this.rte.dom.attr(i, 'alt'));
		this.src.main.width.val(this.origW);
		this.src.main.height.val(this.origH);
		this.src.adv['class'].val(this.rte.dom.attr(i, 'class'));
		this.src.main.margin.val(this.prevImg)
		var f = this.prevImg.css('float');
		this.src.main.align.val(f == 'left' || f == 'right' ? f : (this.prevImg.css('vertical-align')||''));
		this.src.main.border.val(this.prevImg)
		this.src.adv.style.val(this.rte.dom.attr(i, 'style'));
	}
	
	/**
	 * ÐžÑ‡Ð¸Ñ‰Ð°ÐµÑ‚ Ð¿Ð¾Ð»Ñ Ñ„Ð¾Ñ€Ð¼Ñ‹
	 *
	 **/
	this.cleanValues = function() {
		$.each(this.src, function() {
			$.each(this, function() {
				var $this = $(this);
				if ($this.attr('name') != 'src') {
					$this.val('');
				}
			});
		});
	}
	
	/**
	 * Ð£ÑÑ‚Ð°Ð½Ð°Ð²Ð»Ð¸Ð²Ð°ÐµÑ‚ Ð°Ñ‚Ñ‚Ñ€Ð¸Ð±ÑƒÑ‚Ñ‹ prevImg Ð¸Ð· Ð¿Ð¾Ð»ÐµÐ¹ Ñ„Ð¾Ñ€Ð¼Ñ‹
	 *
	 **/
	this.updateImg = function() {
		this.prevImg.attr({
				style  : $.trim(this.src.adv.style.val()),
				title  : $.trim(this.src.main.title.val()),
				alt    : $.trim(this.src.main.alt.val()),
				width  : parseInt(this.src.main.width.val()),
				height : parseInt(this.src.main.height.val())
			});

		var a = this.src.main.align.val();
		var f = a == 'left' || a == 'right' ? a : '';
		
		var b = this.src.main.border.val(); 
		var m = this.src.main.margin.val();
		this.prevImg.css('float', f);
		this.prevImg.css('vertical-align', f ? '' : a);
		this.prevImg.css('border', $.trim(b.width+' '+b.style+' '+b.color));
		if (m.css) {
			this.prevImg.css('margin', m.css);
		} else {
			this.prevImg.css('margin-top', m.top);
			this.prevImg.css('margin-right', m.right);
			this.prevImg.css('margin-bottom', m.bottom);
			this.prevImg.css('margin-left', m.left);						
		}

		$.each([this.src.events, this.src.adv], function() {
			$.each(this, function() {
				var $this = $(this);
				var n = $this.attr('name');
				if (n != 'style') {
					var v = $.trim($this.val());
					if (v) {
						self.prevImg.attr(n, v);
					} else {
						self.prevImg.removeAttr(n);
					}
				}
			});
		});
		
	}
	
	/**
	 * ÐžÐ±Ð½Ð¾Ð²Ð»ÑÐµÑ‚ Ñ„Ð¾Ñ€Ð¼Ñƒ Ð²Ñ‹Ð±Ð¾Ñ€Ð° Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ
	 *
	 **/
	this.updatePreview = function() {
		
		var imgsrc = this.prevImg ? this.prevImg.attr('src') : '';
		var src    = $.trim(this.src.main.src.val());
		if (!src || src !=imgsrc) { // new image or empty src
			if (this.prevImg) {
				this.prevImg.remove();
				this.prevImg = null;
			}
			this.cleanValues();
			if (src) {  // new image
				
				this.prevImg = $(this.frame.createElement('img'))
					.attr('src',  this.rte.utils.absoluteURL(src))
					.bind('load', function() {
						self.updateValues();
					})
				this.preview.prepend(this.prevImg);
				self.updateValues();
			}
		} else { // update existsed image
			this.updateImg();
		}
	}
	
	this.updateDimesions = function(e) {
		
		var w = parseInt(this.src.main.width.val())  || 0;
		var h = parseInt(this.src.main.height.val()) || 0;
		if (w > 0 && h > 0) {
			if (e.currentTarget == this.src.main.width.get(0)) {
				
				this.src.main.height.val(parseInt(w*this.origH/this.origW));
			} else {
				this.src.main.width.val(parseInt(h*this.origW/this.origH));
			}	
		} else {
			this.src.main.width.val(this.origW);
			this.src.main.height.val(this.origH);			
		}

		this.updateImg();

	}
	
	this.set = function() {
		
		if (!this.prevImg || !this.prevImg.attr('width')) {
			this.img  && this.img.remove();
			this.link && this.rte.doc.execCommand('unlink', false, null);
		} else {
			if (!this.img.parents().length) {
				this.rte.browser.msie && this.rte.selection.restoreIERange();
				this.img = $(this.rte.doc.createElement('img'));
			}
			this.img.attr({
					src    : this.rte.utils.absoluteURL($.trim(this.src.main.src.val())),
					style  : $.trim(this.rte.dom.attr(this.prevImg.get(0), 'style')),
					title  : $.trim(this.src.main.title.val()),
					alt    : $.trim(this.src.main.alt.val()),
					width  : parseInt(this.src.main.width.val()),
					height : parseInt(this.src.main.height.val())
				});
				
			for (var _n in this.src.adv) {
				if (_n != 'style') {
					var val = this.src.adv[_n].val();
					if (val) {
						this.img.attr(_n, val);
					} else {
						this.img.removeAttr(_n)
					}
					
				}
			}
			for (var _n in this.src.events) {
				var val = this.src.events[_n].val();
				if (val) {
					this.img.attr(_n, val);
				} else {
					this.img.removeAttr(_n)
				}
			}
				
			if (!this.img.parents().length) {
				this.rte.selection.insertNode(this.img.get(0))
			}

			// Link
			// var href   = this.rte.utils.absoluteURL(this.src.link.href.val());
			// var title  = $.trim(this.src.link.title.val());
			// if (!href) {
			// 	if (this.link) {
			// 		this.link.replaceWith(this.prevImg);
			// 	}
			// } else {
			// 	if (this.link) {
			// 		this.link.attr('href', href).removeAttr('target').removeAttr('title');
			// 		title  && this.alink.attr('title', title);
			// 	} else {
			// 		this.link = $(this.rte.doc.createElement('a')).attr('href', href);
			// 		title  && this.link.attr('title', title);
			// 		this.prevImg.wrap(this.link);
			// 	}
			// }
		}
		this.rte.ui.update();
	}

	this.update = function() {
		this.domElem.removeClass('disabled');
		var n = this.rte.selection.getEnd();
		if (n.nodeName == 'IMG') {
			this.domElem.addClass('active');
		} else {
			this.domElem.removeClass('active');
		}
	}
	
}

/**
 * @class Ð£Ð²ÐµÐ»Ð¸Ñ‡ÐµÐ½Ð¸Ðµ Ð¾Ñ‚ÑÑ‚ÑƒÐ¿Ð°
 * ÑÐ¿Ð¸ÑÐºÐ¸ - ÐµÑÐ»Ð¸ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½ Ð¾Ð´Ð¸Ð½ ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚ - ÑƒÐ²ÐµÐ»Ð¸Ñ‡Ð¸Ð²Ð°ÐµÑ‚ÑÑ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ð¾ÑÑ‚ÑŒ ÑÐ¿Ð¸ÑÐºÐ°, Ð² Ð¾ÑÑ‚Ð°Ð»ÑŒÐ½Ñ‹Ñ… ÑÐ»ÑƒÑ‡Ð°ÑÑ… - padding Ñƒ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÑÐºÐ¾Ð³Ð¾ ul|ol
 * Ð•ÑÐ»Ð¸ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ð° Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð° Ð¿Ð¾Ð»Ð½Ð¾ÑÑ‚ÑŒÑŽ - ÐµÐ¹ Ð´Ð¾Ð±Ð°Ð²Ð»ÑÐµÑ‚ÑÑ margin, ÐµÑÐ»Ð¸ Ñ‡Ð°ÑÑ‚Ð¸Ñ‡Ð½Ð¾ - ÑƒÐ²ÐµÐ»Ð¸Ñ‡Ð¸Ð²Ð°ÐµÑ‚ÑÑ padding Ð´Ð»Ñ ÑÑ‡ÐµÐµÐº
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.indent = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	this.command = function() {
		var nodes = this.rte.selection.selected({collapsed : true, blocks : true, wrap : 'inline', tag : 'p'});

		function indent(n) {
			var css = /(IMG|HR|TABLE|EMBED|OBJECT)/.test(n.nodeName) ? 'margin-left' : 'padding-left';
			var val = self.rte.dom.attr(n, 'style').indexOf(css) != -1 ? parseInt($(n).css(css))||0 : 0;
			$(n).css(css, val+40+'px');
		}
		
		for (var i=0; i < nodes.length; i++) {
			if (/^(TABLE|THEAD|TFOOT|TBODY|COL|COLGROUP|TR)$/.test(nodes[i].nodeName)) {
				$(nodes[i]).find('td,th').each(function() {
					indent(this);
				});
			} else if (/^LI$/.test(nodes[i].nodeName)) {
				var n = $(nodes[i]);
				$(this.rte.dom.create(nodes[i].parentNode.nodeName))
					.append($(this.rte.dom.create('li')).html(n.html()||'')).appendTo(n.html('&nbsp;'));
			} else {
				indent(nodes[i]);
			}
		};
		this.rte.ui.update();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
	}

}

/**
 * @class button - justify text
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.justifyleft = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);

	this.command = function() {
		this.constructor.prototype.command.call(this);

		var v = this.name == 'justifyfull' ? 'justify' : this.name.replace('justify', '');
		// Ð² Ð¾Ð¿ÐµÑ€Ðµ Ð·Ð°Ð¼ÐµÐ½ÑÐµÐ¼ align Ð½Ð° style
		// if (this.rte.browser.opera || this.rte.browser.msie) {
			$(this.rte.doc.body).find('[align]').each(function() {
				$(this).removeAttr('align').css('text-align', v);
			});
		// }
		// Ð² Ñ„Ñ„ ÑƒÐ±Ð¸Ñ€Ð°ÐµÐ¼ Ð¿ÑƒÑÑ‚Ñ‹Ðµ Ð´Ð¸Ð²Ñ‹
		if (this.rte.browser.mozilla) {
			$(this.rte.doc.body).find("div[style]").each(function() {
				var $this = $(this);
				if ($this.attr('style') == 'text-align: '+v+';' && !$this.children().length && $.trim($this.text()).length == 0) {
					$this.remove();
				}
			});
		}
	}
}

elRTE.prototype.ui.prototype.buttons.justifycenter = elRTE.prototype.ui.prototype.buttons.justifyleft;
elRTE.prototype.ui.prototype.buttons.justifyright  = elRTE.prototype.ui.prototype.buttons.justifyleft;
elRTE.prototype.ui.prototype.buttons.justifyfull   = elRTE.prototype.ui.prototype.buttons.justifyleft;

/**
 * @class button - insert/edit link (open dialog window)
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.link = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	
	function init() {
		self.labels = {
			id        : 'ID',
			'class'   : 'Css class',
			style     : 'Css style',
			dir       : 'Script direction',
			lang      : 'Language',
			charset   : 'Charset',
			type      : 'Target MIME type',
			rel       : 'Relationship page to target (rel)',
			rev       : 'Relationship target to page (rev)',
			tabindex  : 'Tab index',
			accesskey : 'Access key'
		}
		self.src = {
			main : {
				href   : $('<input type="text" />'),
				title  : $('<input type="text" />'),
				anchor : $('<select />').attr('name', 'anchor')//,
				// target : $('<select />')
				// 	.append($('<option />').text(self.rte.i18n('In this window')).val(''))
				// 	.append($('<option />').text(self.rte.i18n('In new window (_blank)')).val('_blank'))
				// 	.append($('<option />').text(self.rte.i18n('In new parent window (_parent)')).val('_parent'))
				// 	.append($('<option />').text(self.rte.i18n('In top frame (_top)')).val('_top'))
			},

			popup : {
				use        : $('<input type="checkbox />"'),
				url        : $('<input type="text" />'    ).val('http://'),
				name       : $('<input type="text" />'    ),
				width      : $('<input type="text" />'    ).attr({size : 6, title : self.rte.i18n('Width')} ).css('text-align', 'right'),
				height     : $('<input type="text" />'    ).attr({size : 6, title : self.rte.i18n('Height')}).css('text-align', 'right'),
				left       : $('<input type="text" />'    ).attr({size : 6, title : self.rte.i18n('Left')}  ).css('text-align', 'right'),
				top        : $('<input type="text" />'    ).attr({size : 6, title : self.rte.i18n('Top')}   ).css('text-align', 'right'),
				location   : $('<input type="checkbox" />'),				
				menubar    : $('<input type="checkbox" />'),
				toolbar    : $('<input type="checkbox" />'),
				scrollbars : $('<input type="checkbox" />'),
				status     : $('<input type="checkbox" />'),
				resizable  : $('<input type="checkbox" />'),
				dependent  : $('<input type="checkbox" />'),
				retfalse   : $('<input type="checkbox" />').attr('checked', true)
			},

			adv : {
				id        : $('<input type="text" />'),
				'class'   : $('<input type="text" />'),
				style     : $('<input type="text" />'),
				dir       : $('<select />')
							.append($('<option />').text(self.rte.i18n('Not set')).val(''))
							.append($('<option />').text(self.rte.i18n('Left to right')).val('ltr'))
							.append($('<option />').text(self.rte.i18n('Right to left')).val('rtl')),
				lang      : $('<input type="text" />'),
				charset   : $('<input type="text" />'),
				type      : $('<input type="text" />'),
				rel       : $('<input type="text" />'),
				rev       : $('<input type="text" />'),
				tabindex  : $('<input type="text" />'),
				accesskey : $('<input type="text" />')
			},
			events : {}
		}

		$.each(
			['onblur', 'onfocus', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onmouseleave', 'onkeydown', 'onkeypress', 'onkeyup'], 
			function() {
				self.src.events[this] = $('<input type="text" />');
		});

		$.each(self.src, function() {
			for (var n in this) {
				this[n].attr('name', n);
				var t = this[n].attr('type');
				if (!t || (t == 'text'  && !this[n].attr('size')) ) {
					this[n].css('width', '100%');
				}
			}
		});
		
	}
	
	this.command = function() {
		!this.src && init();
		this.rte.browser.msie && this.rte.selection.saveIERange();
		
		var n = this.rte.selection.getNode();
		var l;
		if ((l = this.rte.dom.selfOrParentLink(n))) {
			this.link = l;
		} else if ((l = this.rte.dom.childLinks(n))) {
			this.link = l[0];
		}
		this.link = this.link ? $(this.link) : $(this.rte.doc.createElement('a'));

		this.updatePopup();
		
		this.src.main.anchor.empty();
		$('a[href!=""][name]', this.rte.doc).each(function() {
			var n = $(this).attr('name');
			self.src.main.anchor.append($('<option />').val(n).text(n));
		});
		if (this.src.main.anchor.children().length) {
			this.src.main.anchor.prepend($('<option />').val('').text(this.rte.i18n('Select bookmark')) )
				.change(function() {
					var v = $(this).val();
					if (v) {
						self.src.main.href.val('#'+v);
					}
				});
		}
		
		var opts = {
			submit : function(e, d) { e.stopPropagation(); e.preventDefault(); self.set(); d.close(); },
			tabs : { show : function(e, ui) { if (ui.index==3) { self.updateOnclick(); } } },
			dialog : {
				width : 'auto',
				width : 430,
				title : this.rte.i18n('Link')
			}
		}
		var d = new elDialogForm(opts);
		
		var l = $('<div />')
			.append( $('<label />').append(this.src.popup.location).append(this.rte.i18n('Location bar')))
			.append( $('<label />').append(this.src.popup.menubar).append(this.rte.i18n('Menu bar')))
			.append( $('<label />').append(this.src.popup.toolbar).append(this.rte.i18n('Toolbar')))				
			.append( $('<label />').append(this.src.popup.scrollbars).append(this.rte.i18n('Scrollbars')));
		var r = $('<div />')
			.append( $('<label />').append(this.src.popup.status).append(this.rte.i18n('Status bar')))
			.append( $('<label />').append(this.src.popup.resizable).append(this.rte.i18n('Resizable')))
			.append( $('<label />').append(this.src.popup.dependent).append(this.rte.i18n('Depedent')))				
			.append( $('<label />').append(this.src.popup.retfalse).append(this.rte.i18n('Add return false')));
		
		d.tab('main', this.rte.i18n('Properies'))
			.tab('popup',  this.rte.i18n('Popup'))
			.tab('adv',    this.rte.i18n('Advanced'))
			.tab('events', this.rte.i18n('Events'))
			.append($('<label />').append(this.src.popup.use).append(this.rte.i18n('Open link in popup window')), 'popup')
			.separator('popup')
			.append([this.rte.i18n('URL'),  this.src.popup.url],  'popup', true)
			.append([this.rte.i18n('Window name'), this.src.popup.name], 'popup', true)
			.append([this.rte.i18n('Window size'), $('<span />').append(this.src.popup.width).append(' x ').append(this.src.popup.height).append(' px')], 'popup', true)
			.append([this.rte.i18n('Window position'), $('<span />').append(this.src.popup.left).append(' x ').append(this.src.popup.top).append(' px')], 'popup', true)				
			.separator('popup')
			.append([l, r], 'popup', true);

		var link = this.link.get(0);
		var href = this.rte.dom.attr(link, 'href');
		this.src.main.href.val(href).change(function() {
			$(this).val(self.rte.utils.absoluteURL($(this).val()));
		});
		
		if (this.rte.options.fmAllow && this.rte.options.fmOpen) {
			var s = $('<span />').append(this.src.main.href.css('width', '87%'))
				.append(
					$('<span />').addClass('ui-state-default ui-corner-all')
						.css({'float' : 'right', 'margin-right' : '3px'})
						.attr('title', self.rte.i18n('Open file manger'))
						.append($('<span />').addClass('ui-icon ui-icon-folder-open'))
							.click( function() {
								self.rte.options.fmOpen( function(url) { self.src.main.href.val(url).change(); } );
							})
							.hover(function() {$(this).addClass('ui-state-hover')}, function() { $(this).removeClass('ui-state-hover')})
				);
			d.append([this.rte.i18n('Link URL'), s], 'main', true);
		} else {
			d.append([this.rte.i18n('Link URL'), this.src.main.href], 'main', true);
		}
		this.src.main.href.change();
		
		d.append([this.rte.i18n('Title'), this.src.main.title.val(this.rte.dom.attr(link, 'title'))], 'main', true);
		if (this.src.main.anchor.children().length) {
			d.append([this.rte.i18n('Bookmark'), this.src.main.anchor.val(href)], 'main', true)
		}

		for (var n in this.src.adv) {
			this.src.adv[n].val(this.rte.dom.attr(link, n));
			d.append([this.rte.i18n(this.labels[n] ? this.labels[n] : n), this.src.adv[n]], 'adv', true);
		}
		for (var n in this.src.events) {
			var v = this.rte.utils.trimEventCallback(this.rte.dom.attr(link, n));
			this.src.events[n].val(v);
			d.append([this.rte.i18n(this.labels[n] ? this.labels[n] : n), this.src.events[n]], 'events', true);
		}
		
		this.src.popup.use.change(function() {
			var c = $(this).attr('checked');
			$.each(self.src.popup, function() {
				if ($(this).attr('name') != 'use') {
					if (c) {
						$(this).removeAttr('disabled');
					} else {
						$(this).attr('disabled', true);
					}
				}
			})
		});
		this.src.popup.use.change();
		d.open();
	}
	
	this.update = function() {
		var n = this.rte.selection.getNode();
		if (this.rte.dom.selfOrParentAnchor(n)) {
			this.domElem.addClass('disabled');	
		} else if (this.rte.dom.selfOrParentLink(n) || this.rte.dom.childLinks(n).length) {
			this.domElem.removeClass('disabled').addClass('active');
		} else {
			this.domElem.removeClass('active');
			if (!this.rte.selection.collapsed() || (n.nodeType == 1 && /^(IMG|EMBED|OBJECT)$/.test(n.nodeName))) {
				this.domElem.removeClass('disabled');
			} else {
				this.domElem.addClass('disabled');
			}
		}
	}
	
	this.updatePopup = function() {
		var onclick = this.rte.dom.attr(this.link.get(0), 'onclick');
		onclick = onclick ? $.trim(onclick.toString()) : ''
		if ( onclick.length>0 && (m = onclick.match(/window.open\("([^"]+)",\s*"([^"]*)",\s*"([^"]*)"\s*.*\);\s*(return\s+false)?/))) {
			this.src.popup.use.attr('checked', 'on')
			this.src.popup.url.val(m[1]);
			this.src.popup.name.val(m[2]);

			if ( /location=yes/.test(m[3]) ) {
				this.src.popup.location.attr('checked', true);
			}
			if ( /menubar=yes/.test(m[3]) ) {
				this.src.popup.menubar.attr('checked', true);
			}
			if ( /toolbar=yes/.test(m[3]) ) {
				this.src.popup.toolbar.attr('checked', true);
			}
			if ( /scrollbars=yes/.test(m[3]) ) {
				this.src.popup.scrollbars.attr('checked', true);
			}
			if ( /status=yes/.test(m[3]) ) {
				this.src.popup.status.attr('checked', true);
			}
			if ( /resizable=yes/.test(m[3]) ) {
				this.src.popup.resizable.attr('checked', true);
			}
			if ( /dependent=yes/.test(m[3]) ) {
				this.src.popup.dependent.attr('checked', true);
			}
			if ((_m = m[3].match(/width=([^,]+)/))) {
				this.src.popup.width.val(_m[1]);
			}
			if ((_m = m[3].match(/height=([^,]+)/))) {
				this.src.popup.height.val(_m[1]);
			}
			if ((_m = m[3].match(/left=([^,]+)/))) {
				this.src.popup.left.val(_m[1]);
			}
			if ((_m = m[3].match(/top=([^,]+)/))) {
				this.src.popup.top.val(_m[1]);
			}
			if (m[4]) {
				this.src.popup.retfalse.attr('checked', true);
			}
		} else {
			$.each(this.src.popup, function() {
				var $this = $(this);
				if ($this.attr('type') == 'text') {
					$this.val($this.attr('name') == 'url' ? 'http://' : '');
				} else {
					if ($this.attr('name') == 'retfalse') {
						this.attr('checked', true);
					} else {
						$this.removeAttr('checked');
					}
				}
			});
		}
		
	}
	
	this.updateOnclick = function () {
		var url = this.src.popup.url.val();
		if (this.src.popup.use.attr('checked') && url) {
			var params = '';
			if (this.src.popup.location.attr('checked')) {
				params += 'location=yes,';
			}
			if (this.src.popup.menubar.attr('checked')) {
				params += 'menubar=yes,';
			}
			if (this.src.popup.toolbar.attr('checked')) {
				params += 'toolbar=yes,';
			}
			if (this.src.popup.scrollbars.attr('checked')) {
				params += 'scrollbars=yes,';
			}
			if (this.src.popup.status.attr('checked')) {
				params += 'status=yes,';
			}
			if (this.src.popup.resizable.attr('checked')) {
				params += 'resizable=yes,';
			}
			if (this.src.popup.dependent.attr('checked')) {
				params += 'dependent=yes,';
			}
			if (this.src.popup.width.val()) {
				params += 'width='+this.src.popup.width.val()+',';
			}
			if (this.src.popup.height.val()) {
				params += 'height='+this.src.popup.height.val()+',';
			}
			if (this.src.popup.left.val()) {
				params += 'left='+this.src.popup.left.val()+',';
			}
			if (this.src.popup.top.val()) {
				params += 'top='+this.src.popup.top.val()+',';
			}
			if (params.length>0) {
				params = params.substring(0, params.length-1)
			}
			var retfalse = this.src.popup.retfalse.attr('checked') ? 'return false;' : '';
			var onclick = 'window.open("'+url+'", "'+$.trim(this.src.popup.name.val())+'", "'+params+'");'+retfalse;
			this.src.events.onclick.val(onclick);
			if (!this.src.main.href.val()) {
				this.src.main.href.val('#');
			}
		} else {
			var v = this.src.events.onclick.val();
			v = v.replace(/window\.open\([^\)]+\)\s*;?\s*return\s*false\s*;?/i, '');
			this.src.events.onclick.val(v);
		}
	}
	
	this.set = function() {
		this.updateOnclick();
		this.rte.browser.msie && this.rte.selection.restoreIERange();
		var href = this.rte.utils.absoluteURL(this.src.main.href.val());
		if (!href) {
			this.link.parents().length && this.rte.doc.execCommand('unlink', false, null);
		} else {
			if (!this.link.parents().length) {
				
				var fakeURL = '#--el-editor---'+Math.random();
				var r =this.rte.doc.execCommand('createLink', false, fakeURL);
				// self.rte.log(r)
				this.link = $('a[href="'+fakeURL+'"]', this.rte.doc);
				this.link.each(function() {
					var $this = $(this);
					// ÑƒÐ´Ð°Ð»ÑÐµÐ¼ ÑÑÑ‹Ð»ÐºÐ¸ Ð²Ð¾ÐºÑ€ÑƒÐ³ Ð¿ÑƒÑÑ‚Ñ‹Ñ… ÑÐ»ÐµÐ¼ÐµÐ½Ñ‚Ð¾Ð²
					if (!$.trim($this.html()) && !$.trim($this.text())) {
						$this.replaceWith($this.text()); //  ÑÐ¾Ñ…Ñ€Ð°Ð½ÑÐµÐ¼ Ð¿Ñ€Ð¾Ð±ÐµÐ»Ñ‹ :)
					}
				});
			}
			this.src.main.href.val(href);
			for (var tab in this.src) {
				if (tab != 'popup') {
					for (var n in this.src[tab]) {
						if (n != 'anchors') {
							var v = $.trim(this.src[tab][n].val());
							if (v) {
								this.link.attr(n, v);
							} else {
								this.link.removeAttr(n);
							}
						}
					}
				}
			};
		}
		this.rte.ui.update(true);
	}
	
}

/**
 * @class button - insert non breakable space
 * Ð•ÑÐ»Ð¸ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ ÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚Ð¾ Ð¸ Ð½Ð°Ñ…Ð¾Ð´Ð¸Ñ‚ÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ div'a - Ð¾Ð½ ÑƒÐ´Ð°Ð»ÑÐµÑ‚ÑÑ
 * ÐÐ¾Ð²Ñ‹Ðµ div'Ñ‹ ÑÐ¾Ð·Ð´Ð°ÑŽÑ‚ÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð¸Ð· Ð½ÐµÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚Ð¾Ð³Ð¾ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.nbsp = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		this.rte.selection.insertHtml('&nbsp;', true);
		this.rte.window.focus();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
	}
}

/**
 * @class button - outdent text
 * ÑƒÐ¼ÐµÐ½ÑŒÑˆÐ°ÐµÑ‚ padding/margin/ÑÐ°Ð¼Ð¾Ð¼Ð½ÐµÐ½Ð¸Ðµ ;)
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 * @todo decrease lists nesting level!
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.outdent = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;

	this.command = function() {
		var v = this.find();
		if (v.node) {
			$(v.node).css(v.type, (v.val>40 ? v.val-40 : 0)+'px');
			this.rte.ui.update();
		}
	}
	
	this.find = function(n) {
		function checkNode(n) {
			var ret = {type : '', val : 0};
			var s;
			if ((s = self.rte.dom.attr(n, 'style'))) {
				ret.type = s.indexOf('padding-left') != -1
					? 'padding-left'
					: (s.indexOf('margin-left') != -1 ? 'margin-left' : '');
				ret.val = ret.type ? parseInt($(n).css(ret.type))||0 : 0;
			}
			return ret;
		}
		
		var n = this.rte.selection.getNode();
		var ret = checkNode(n);
		if (ret.val) {
			ret.node = n;
		} else {
			$.each(this.rte.dom.parents(n, '*'), function() {
				ret = checkNode(this);
				if (ret.val) {
					ret.node = this;
					return ret;
				}
			})
		}
		return ret;
	}
	
	this.update = function() {
		var v = this.find();
		if (v.node) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}

	
}

/**
 * @class button - insert formatted text (open dialog window)
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.pasteformattext = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	this.iframe = $(document.createElement('iframe')).addClass('el-rte-paste-input')
	this.doc    = null;
	var self    = this;
	
	this.command = function() {
		this.rte.browser.msie && this.rte.selection.saveIERange();
		var opts = {
			submit : function(e, d) {
				e.stopPropagation();
				e.preventDefault();
				self.paste();
				d.close();
			},
			dialog : {
				width : 500,
				title : this.rte.i18n('Paste formatted text')
			}
		}
		var d = new elDialogForm(opts);
		d.append(this.iframe).open();
		this.doc = this.iframe.get(0).contentWindow.document;
		html = this.rte.options.doctype
			+'<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
		if (this.rte.options.cssfiles.length) {
			$.each(this.rte.options.cssfiles, function() {
				html += '<link rel="stylesheet" type="text/css" href="'+this+'" />';
			});
		}
		html += '</head><body>  </body></html>';	
		
		this.doc.open();
		this.doc.write(html);
		this.doc.close();
		if (!this.rte.browser.msie) {
			try { this.doc.designMode = "on"; } 
			catch(e) { }
		} else {
			this.doc.body.contentEditable = true;
		}
		this.iframe.get(0).contentWindow.focus();

	}
	
	this.paste = function() {
		var html = $.trim($(this.doc.body).html());
		if (html) {
			this.rte.browser.msie && this.rte.selection.restoreIERange();
			this.rte.selection.insertHtml(this.rte.filter(html), true);
			this.rte.ui.update(true);
		}
	}

	this.update = function() {
		this.domElem.removeClass('disabled');
	}
}

/**
 * @class ÐºÐ½Ð¾Ð¿ÐºÐ° "Ð²ÑÑ‚Ð°Ð²Ð¸Ñ‚ÑŒ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ñ‚ÐµÐºÑÑ‚" 
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.pastetext = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	this.input = $('<textarea />').addClass('el-rte-paste-input');
	var self   = this;
	
	this.command = function() {
		this.rte.browser.msie && this.rte.selection.saveIERange();
		var opts = {
			submit : function(e, d) {
				e.stopPropagation();
				e.preventDefault();
				self.paste();
				d.close();
			},
			dialog : {
				width : 500,
				title : this.rte.i18n('Paste only text')
			}
		}
		var d = new elDialogForm(opts);
		d.append(this.input).open();
	}
	
	this.paste = function() {
		var txt = $.trim(this.input.val());
		if (txt) {
			this.rte.browser.msie && this.rte.selection.restoreIERange();
			this.rte.selection.insertText(txt.replace(/\r?\n/g, '<br />'), true);
			this.rte.ui.update(true);
		}
		this.input.val('');
	}

	this.update = function() {
		this.domElem.removeClass('disabled');
	}
	
}

/**
 * @class button - save editor content (submit form)
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.save = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	this.active = true;
	
	this.command = function() {
		this.rte.save();
	}
	
	this.update = function() { }
}

/**
 * @class button - stops elements floating. Insert div with style="clear:all"
 * Ð•ÑÐ»Ð¸ Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ðµ ÑÑ…Ð»Ð¾Ð¿Ð½ÑƒÑ‚Ð¾ Ð¸ Ð½Ð°Ñ…Ð¾Ð´Ð¸Ñ‚ÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ð¸ div'a Ñ Ð°Ñ‚Ñ‚Ñ€Ð¸Ð±ÑƒÑ‚Ð¾Ð¼ Ð¸Ð»Ð¸ css clear - Ð¾Ð½ ÑƒÐ´Ð°Ð»ÑÐµÑ‚ÑÑ
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.stopfloat = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);

	this.find = function() {
		if (this.rte.selection.collapsed()) {
			var n = this.rte.dom.selfOrParent(this.rte.selection.getEnd(), /^DIV$/);
			if (n && (this.rte.dom.attr(n, 'clear') || $(n).css('clear') != 'none')) {
				return n;
			}
		}
	}
	
	this.command = function() {
		var n;
		if ((n = this.find())) {
			var n = $(n);
			if (!n.children().length && !$.trim(n.text()).length) {
				n.remove();
			} else {
				n.removeAttr('clear').css('clear', '');
			}
		} else {
			this.rte.selection.insertNode($(this.rte.dom.create('div')).css('clear', 'both').get(0), true);
		}
		this.rte.ui.update(true);
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		if (this.find()) {
			this.domElem.addClass('active');
		} else {
			this.domElem.removeClass('active');
		}
	}
}

/**
 * @class button - create/edit table (open dialog window)
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * Copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.table = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self    = this;
	this.src    = null;
	this.labels = null;
	
	function init() {
		self.labels = {
			main      : 'Properies',
			adv       : 'Advanced',
			events    : 'Events',
			id        : 'ID',
			'class'   : 'Css class',
			style     : 'Css style',
			dir       : 'Script direction',
			summary   : 'Summary',
			lang      : 'Language',
			href      : 'URL'
		}
		
		self.src = {
			main : {
				caption : $('<input type="text" />'),
				rows    : $('<input type="text" />').attr('size', 5).val(2),
				cols    : $('<input type="text" />').attr('size', 5).val(2),
				width   : $('<input type="text" />').attr('size', 5),
				wunit   : $('<select />')
							.append($('<option />').val('%').text('%'))
							.append($('<option />').val('px').text('px')),				
				height  : $('<input type="text" />').attr('size', 5),	
				hunit   : $('<select />')
							.append($('<option />').val('%').text('%'))
							.append($('<option />').val('px').text('px')),	
				align   : $('<select />')
							.append($('<option />').val('').text(self.rte.i18n('Not set')))
							.append($('<option />').val('left').text(self.rte.i18n('Left')))
							.append($('<option />').val('center').text(self.rte.i18n('Center')))	
							.append($('<option />').val('right').text(self.rte.i18n('Right'))),	
				spacing : $('<input type="text" />').attr('size', 5),	
				padding : $('<input type="text" />').attr('size', 5),
				border  : $('<div />'),
				// frame   : $('<select />')
				// 			.append($('<option />').val('void').text(self.rte.i18n('No')))
				// 			.append($('<option />').val('border').text(self.rte.i18n('Yes'))),
				rules   : $('<select />')
							.append($('<option />').val('none').text(self.rte.i18n('No')))
							.append($('<option />').val('all').text(self.rte.i18n('Cells')))
							.append($('<option />').val('groups').text(self.rte.i18n('Groups')))
							.append($('<option />').val('rows').text(self.rte.i18n('Rows')))
							.append($('<option />').val('cols').text(self.rte.i18n('Columns'))),
				margin  : $('<div />'),
				bg      : $('<div />'),
				bgimg   : $('<input type="text" />').css('width', '90%')
			},
			
			adv : {
				id        : $('<input type="text" />'),
				summary   : $('<input type="text" />'),
				'class'   : $('<input type="text" />'),
				style     : $('<input type="text" />'),
				dir       : $('<select />')
								.append($('<option />').text(self.rte.i18n('Not set')).val(''))
								.append($('<option />').text(self.rte.i18n('Left to right')).val('ltr'))
								.append($('<option />').text(self.rte.i18n('Right to left')).val('rtl')),
				lang      : $('<input type="text" />')
			},
			
			events : {}
		}
		
		$.each(self.src, function() {
			for (var n in this) {
				this[n].attr('name', n);
				var t = this[n].get(0).nodeName; 
				if (t == 'INPUT' && n != 'bgimg') {
					this[n].css(this[n].attr('size') ? {'text-align' : 'right'} : {width : '100%'});
				} else if (t == 'SELECT' && n!='wunit' && n!='hunit') {
					this[n].css('width', '100%');
				}
			}
		});
		
		$.each(
			['onblur', 'onfocus', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onmouseleave', 'onkeydown', 'onkeypress', 'onkeyup'], 
			function() {
				self.src.events[this] = $('<input type="text" />').attr('name', this).css('width', '100%');
		});
		
		self.src.main.align.change(function() {
			var v = $(this).val();
			if (v == 'center') {
				self.src.main.margin.val({left : 'auto', right : 'auto'});
			} else {
				var m = self.src.main.margin.val();
				if (m.left == 'auto' && m.right == 'auto') {
					self.src.main.margin.val({left : '', right : ''});
				}
			}
		});
		
		self.src.main.bgimg.change(function() {
			var t = $(this);
			t.val(self.rte.utils.absoluteURL(t.val()));
		})
		
	}
	
	this.command = function() {
		var n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^TABLE$/);
		
		if (this.name == 'table') {
			this.table = $(this.rte.doc.createElement('table'));	
		} else {
			this.table = n ? $(n) : $(this.rte.doc.createElement('table'));					
		}
		
		!this.src && init();
		this.src.main.border.elBorderSelect({styleHeight : 117});
		this.src.main.bg.elColorPicker({palettePosition : 'outer', 'class' : 'el-colorpicker ui-icon ui-icon-pencil'});
		this.src.main.margin.elPaddingInput({ type : 'margin', value : this.table});
		
		if (this.table.parents().length) {
			this.src.main.rows.val('').attr('disabled', true);
			this.src.main.cols.val('').attr('disabled', true);
		} else {
			this.src.main.rows.val(2).removeAttr('disabled');
			this.src.main.cols.val(2).removeAttr('disabled');
		}
		
		var w = this.table.css('width') || this.table.attr('width');
		this.src.main.width.val(parseInt(w)||'');
		this.src.main.wunit.val(w.indexOf('px') != -1 ? 'px' : '%');
		
		var h = this.table.css('height') || this.table.attr('height');	
		this.src.main.height.val(parseInt(h)||'');
		this.src.main.hunit.val(h && h.indexOf('px') != -1 ? 'px' : '%');

		var f = this.table.css('float');
		this.src.main.align.val('');
		if (f == 'left' || f == 'right') {
			this.src.main.align.val(f);
		} else {
			var ml = this.table.css('margin-left');
			var mr = this.table.css('margin-right');
			if (ml == 'auto' && mr == 'auto') {
				this.src.main.align.val('center');
			}
		}

		this.src.main.border.val(this.table);
		//this.src.main.frame.val(this.table.attr('frame'));
		this.src.main.rules.val(this.rte.dom.attr(this.table.get(0), 'rules'));

		this.src.main.bg.val(this.table.css('background-color'));
		var bgimg = this.table.css('background-image').replace(/url\(([^\)]+)\)/i, "$1");
		this.src.main.bgimg.val(bgimg!='none' ? bgimg : '');

		var opts = {
			submit : function(e, d) { e.stopPropagation(); e.preventDefault(); self.set(); d.close(); },
			dialog : {
				width : 530,
				title : this.rte.i18n('Table')
			}
		}
		var d = new elDialogForm(opts);
		
		for (var tab in this.src) {
			d.tab(tab, this.rte.i18n(this.labels[tab]));
			if (tab == 'main') {
				var t1 = $('<table />')
					.append($('<tr />').append('<td>'+this.rte.i18n('Rows')+'</td>').append($('<td />').append(this.src.main.rows)))
					.append($('<tr />').append('<td>'+this.rte.i18n('Columns')+'</td>').append($('<td />').append(this.src.main.cols)));
				var t2 = $('<table />')
					.append($('<tr />').append('<td>'+this.rte.i18n('Width')+'</td>').append($('<td />').append(this.src.main.width).append(this.src.main.wunit)))
					.append($('<tr />').append('<td>'+this.rte.i18n('Height')+'</td>').append($('<td />').append(this.src.main.height).append(this.src.main.hunit)));
				var t3 = $('<table />')
					.append($('<tr />').append('<td>'+this.rte.i18n('Spacing')+'</td>').append($('<td />').append(this.src.main.spacing.val(this.table.attr('cellspacing')||''))))
					.append($('<tr />').append('<td>'+this.rte.i18n('Padding')+'</td>').append($('<td />').append(this.src.main.padding.val(this.table.attr('cellpadding')||''))));
				
				d.append([this.rte.i18n('Caption'), this.src.main.caption.val(this.table.find('caption').eq(0).text() || '')], 'main', true)
					.separator('main')
					.append([t1, t2, t3], 'main', true)
					.separator('main')
					.append([this.rte.i18n('Border'),        this.src.main.border], 'main', true)
					//.append([this.rte.i18n('Frame'),       this.src.main.frame], 'main', true)
					.append([this.rte.i18n('Inner borders'), this.src.main.rules], 'main', true)
					.append([this.rte.i18n('Alignment'),     this.src.main.align], 'main', true)
					.append([this.rte.i18n('Margins'),       this.src.main.margin], 'main', true)
					.append([this.rte.i18n('Background'),    $('<span />').append($('<span />').css({'float' : 'left', 'margin-right' : '3px'}).append(this.src.main.bg)).append(this.src.main.bgimg)], 'main', true)
			} else {
				for (var name in this.src[tab]) {
					var v = this.rte.dom.attr(this.table, name);
					if (tab == 'events') {
						v = this.rte.utils.trimEventCallback(v);
					} 
					d.append([this.rte.i18n(this.labels[name] ? this.labels[name] : name), this.src[tab][name].val(v)], tab, true);
				}
			}
		}
		
		d.open();
	}
	
	this.set = function() {
		
		if (!this.table.parents().length) {
			var r = parseInt(this.src.main.rows.val()) || 0;
			var c = parseInt(this.src.main.cols.val()) || 0;
			if (r<=0 || c<=0) {
				return;
			}
			
			var b = $(this.rte.doc.createElement('tbody')).appendTo(this.table);
			var tr     = $('<tr />');
			for (var i=0; i < c; i++) {
				tr.append($('<td />').html('&nbsp;'));
			};
			for (var i=0; i<r; i++) {
				b.append(tr.clone(true))
			};
			this.rte.selection.insertNode(this.table.get(0), true);
		} else {
			this.table
				.removeAttr('width')
				.removeAttr('height')
				.removeAttr('border')
				.removeAttr('align')
				.removeAttr('bordercolor')
				.removeAttr('bgcolor')
				.removeAttr('cellspacing')
				.removeAttr('cellpadding')
				.removeAttr('frame')
				.removeAttr('rules')
				.removeAttr('style');
		}
		
		var cap = $.trim(this.src.main.caption.val());
		if (cap) {
			if (!this.table.children('caption').length) {
				this.table.prepend($('<caption />'));
			}
			this.table.children('caption').text(cap)
		} else {
			this.table.children('caption').remove();
		}
		
		for (var tab in this.src) {
			if (tab != 'main') {
				for (var n in this.src[tab]) {
					var v = $.trim(this.src[tab][n].val());
					if (v) {
						this.table.attr(n, v);
					} else {
						this.table.removeAttr(n);
					}
				}
			}
		}
		var spacing, padding, rules;

		if ((spacing = parseInt(this.src.main.spacing.val())) && spacing>=0) {
			this.table.attr('cellspacing', spacing);
		} 

		if ((padding = parseInt(this.src.main.padding.val())) && padding>=0) {
			this.table.attr('cellpadding', padding);
		} 
		
		if ((rules = this.src.main.rules.val())) {
			this.table.attr('rules', rules);
		}
		
		var
			w = parseInt(this.src.main.width.val()) || '',
			h = parseInt(this.src.main.height.val()) || '',
			i = $.trim(this.src.main.bgimg.val()),
			b = this.src.main.border.val(),
			m = this.src.main.margin.val(),
			f = this.src.main.align.val();
		this.table.css({
			width              : w ? w+this.src.main.wunit.val() : '',
			height             : h ? h+this.src.main.hunit.val() : '',
			border             : $.trim(b.width+' '+b.style+' '+b.color),
			'background-color' : this.src.main.bg.val(),
			'background-image' : i ? 'url('+i+')' : ''
		});	
		if (m.css) {
			this.table.css('margin', m.css);
		} else {
			this.table.css({
				'margin-top'    : m.top,
				'margin-right'  : m.right,
				'margin-bottom' : m.bottom,
				'margin-left'   : m.left
			});
		}
		if ((f=='left' || f=='right') && this.table.css('margin-left')!='auto'  && this.table.css('margin-right')!='auto') {
			this.table.css('float', f);
		}
		if (!this.table.attr('style')) {
			this.table.removeAttr('style');
		}
		
		this.rte.ui.update();
	}
	
	this.update = function() {
		this.domElem.removeClass('disabled');
		if (this.name == 'tableprops' && !this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^TABLE$/)) {
			this.domElem.addClass('disabled').removeClass('active');
		}
	}
	
}

elRTE.prototype.ui.prototype.buttons.tableprops = elRTE.prototype.ui.prototype.buttons.table;/**
 * @class button - remove table
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.tablerm = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		var t = this.rte.dom.parent(this.rte.selection.getNode(), /^TABLE$/);
		t && $(t).remove();
		this.rte.ui.update(true);
	}
	
	this.update = function() {
		if (this.rte.dom.parent(this.rte.selection.getNode(), /^TABLE$/)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

/**
 * @class button - table cell properties
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.tbcellprops = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	this.src = null;
	this.labels = null;
	
	function init() {
		self.labels = {
			main    : 'Properies',
			adv     : 'Advanced',
			events  : 'Events',
			id      : 'ID',
			'class' : 'Css class',
			style   : 'Css style',
			dir     : 'Script direction',
			lang    : 'Language'
		}
		
		self.src = {
			main : {
				type    : $('<select />').css('width', '100%')
							.append($('<option />').val('td').text(self.rte.i18n('Data')))
							.append($('<option />').val('th').text(self.rte.i18n('Header'))),
				width   : $('<input type="text" />').attr('size', 4),
				wunit   : $('<select />')
							.append($('<option />').val('%').text('%'))
							.append($('<option />').val('px').text('px')),				
				height  : $('<input type="text" />').attr('size', 4),	
				hunit   : $('<select />')
							.append($('<option />').val('%').text('%'))
							.append($('<option />').val('px').text('px')),	
				align   : $('<select />').css('width', '100%')
							.append($('<option />').val('').text(self.rte.i18n('Not set')))
							.append($('<option />').val('left').text(self.rte.i18n('Left')))
							.append($('<option />').val('center').text(self.rte.i18n('Center')))	
							.append($('<option />').val('right').text(self.rte.i18n('Right')))
							.append($('<option />').val('justify').text(self.rte.i18n('Justify'))),	
				border  : $('<div />'),
				padding  : $('<div />'),
				bg      : $('<div />'),
				bgimg   : $('<input type="text" />').css('width', '90%'),
				apply   : $('<select />').css('width', '100%')
							.append($('<option />').val('').text(self.rte.i18n('Current cell')))
							.append($('<option />').val('row').text(self.rte.i18n('All cells in row')))
							.append($('<option />').val('column').text(self.rte.i18n('All cells in column')))	
							.append($('<option />').val('table').text(self.rte.i18n('All cells in table')))
			},
			
			adv : {
				id        : $('<input type="text" />'),
				'class'   : $('<input type="text" />'),
				style     : $('<input type="text" />'),
				dir       : $('<select />').css('width', '100%')
								.append($('<option />').text(self.rte.i18n('Not set')).val(''))
								.append($('<option />').text(self.rte.i18n('Left to right')).val('ltr'))
								.append($('<option />').text(self.rte.i18n('Right to left')).val('rtl')),
				lang      : $('<input type="text" />')
			},
			
			events : {}
		}
		
		$.each(self.src, function() {
			for (var n in this) {
				this[n].attr('name', n);
				if (this[n].attr('type') == 'text' && !this[n].attr('size') && n!='bgimg') {
					this[n].css('width', '100%')
				}
			}
		});
		
		$.each(
			['onblur', 'onfocus', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmouseout', 'onmouseleave', 'onkeydown', 'onkeypress', 'onkeyup'], 
			function() {
				self.src.events[this] = $('<input type="text" />').attr('name', this).css('width', '100%');
		});
		
	}
	
	this.command = function() {
		!this.src && init();
		this.cell = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(TD|TH)$/);
		if (!this.cell) {
			return;
		}
		this.src.main.type.val(this.cell.nodeName.toLowerCase());
		this.cell = $(this.cell);
		this.src.main.border.elBorderSelect({styleHeight : 117, value : this.cell});
		this.src.main.bg.elColorPicker({palettePosition : 'outer', 'class' : 'el-colorpicker ui-icon ui-icon-pencil'});
		this.src.main.padding.elPaddingInput({ value : this.cell});
		
		var w = this.cell.css('width') || this.cell.attr('width');
		this.src.main.width.val(parseInt(w)||'');
		this.src.main.wunit.val(w.indexOf('px') != -1 ? 'px' : '%');
		
		var h = this.cell.css('height') || this.cell.attr('height');	
		this.src.main.height.val(parseInt(h)||'');
		this.src.main.hunit.val(h.indexOf('px') != -1 ? 'px' : '%');
		
		this.src.main.align.val(this.cell.attr('align') || this.cell.css('text-align'));
		this.src.main.bg.val(this.cell.css('background-color'));
		var bgimg = this.cell.css('background-image');
		this.src.main.bgimg.val(bgimg && bgimg!='none' ? bgimg.replace(/url\(([^\)]+)\)/i, "$1") : '');
		this.src.main.apply.val('');
		
		var opts = {
			submit : function(e, d) { e.stopPropagation(); e.preventDefault(); self.set(); d.close(); },
			dialog : {
//				width : 471,
				width : 'auto',
				title : this.rte.i18n('Table cell properties')
			}
		}
		var d = new elDialogForm(opts);
		for (var tab in this.src) {
			d.tab(tab, this.rte.i18n(this.labels[tab]));
			
			if (tab == 'main') {
				d.append([this.rte.i18n('Width'),              $('<span />').append(this.src.main.width).append(this.src.main.wunit)],  'main', true)
					.append([this.rte.i18n('Height'),          $('<span />').append(this.src.main.height).append(this.src.main.hunit)], 'main', true)
					.append([this.rte.i18n('Table cell type'), this.src.main.type],    'main', true)
					.append([this.rte.i18n('Border'),          this.src.main.border],  'main', true)
					.append([this.rte.i18n('Alignment'),       this.src.main.align],   'main', true)
					.append([this.rte.i18n('Paddings'),        this.src.main.padding], 'main', true)
					.append([this.rte.i18n('Background'),      $('<span />').append($('<span />').css({'float' : 'left', 'margin-right' : '3px'}).append(this.src.main.bg)).append(this.src.main.bgimg)],  'main', true)
					.append([this.rte.i18n('Apply to'),        this.src.main.apply],   'main', true);
			} else {
				for (var name in this.src[tab]) {
					var v = this.cell.attr(name) || '';
					if (tab == 'events') {
						v = this.rte.utils.trimEventCallback(v);
					} 
					d.append([this.rte.i18n(this.labels[name] ? this.labels[name] : name), this.src[tab][name].val(v)], tab, true);
				}
			}
		}
		d.open()
	}
	
	this.set = function() {
		
		var target = this.cell;
		var apply  = this.src.main.apply.val();
		switch (this.src.main.apply.val()) {
			case 'row':
				target = this.cell.parent('tr').children('td,th');
				break;
				
			case 'column':
				target = $(this.rte.dom.tableColumn(this.cell.get(0)));
				break;
				
			case 'table':
				target = this.cell.parents('table').find('td,th');
				break;
		}

		for (var tab in this.src) {
			if (tab != 'main') {
				for (var n in this.src[tab]) {
					var v = $.trim(this.src[tab][n].val());
					if (v) {
						target.attr(n, v);
					} else {
						target.removeAttr(n);
					}
				}
			}
		}
		
		target.removeAttr('width')
			.removeAttr('height')
			.removeAttr('border')
			.removeAttr('align')
			.removeAttr('bordercolor')
			.removeAttr('bgcolor');
			
		var t = this.src.main.type.val();
		var w = parseInt(this.src.main.width.val()) || '';
		var h = parseInt(this.src.main.height.val()) || '';
		var i = $.trim(this.src.main.bgimg.val());
		var b = this.src.main.border.val();
		var css = {
			'width'            : w ? w+this.src.main.wunit.val() : '',
			'height'           : h ? h+this.src.main.hunit.val() : '',
			'background-color' : this.src.main.bg.val(),
			'background-image' : i ? 'url('+i+')' : '',
			'border'           : $.trim(b.width+' '+b.style+' '+b.color),
			'text-align'       : this.src.main.align.val() || ''
		};
		var p = this.src.main.padding.val();
		if (p.css) {
			css.padding = p.css;
		} else {
			css['padding-top']    = p.top;
			css['padding-right']  = p.right;
			css['padding-bottom'] = p.bottom;
			css['padding-left']   = p.left;
		}
		
		target = target.get();

		$.each(target, function() {
			var type = this.nodeName.toLowerCase();
			var $this = $(this);
			if (type != t) {
				
				var attr = {}
				for (var i in self.src.adv) {
					var v = $this.attr(i)
					if (v) {
						attr[i] = v.toString();
					}
				}
				for (var i in self.src.events) {
					var v = $this.attr(i)
					if (v) {
						attr[i] = v.toString();
					}
				}
				var colspan = $this.attr('colspan')||1;
				var rowspan = $this.attr('rowspan')||1;
				if (colspan>1) {
					attr.colspan = colspan;
				}
				if (rowspan>1) {
					attr.rowspan = rowspan;
				}
				
				$this.replaceWith($('<'+t+' />').html($this.html()).attr(attr).css(css) );
				
			} else {
				$this.css(css);
			}
		});

		this.rte.ui.update();
	}
	
	this.update = function() {
		if (this.rte.dom.parent(this.rte.selection.getNode(), /^TABLE$/)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
	
}

/**
 * @class button - table cells merge
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.tbcellsmerge = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	
	function selectedCells() {
		var c1 = self.rte.dom.selfOrParent(self.rte.selection.getStart(), /^(TD|TH)$/);
		var c2 = self.rte.dom.selfOrParent(self.rte.selection.getEnd(), /^(TD|TH)$/);		
		if (c1 && c2 && c1!=c2 && $(c1).parents('table').get(0) == $(c2).parents('table').get(0)) {
			return [c1, c2];
		}
		return null;
	}
	
	this.command = function() {
		var cells = selectedCells();

		if (cells) {
			
			var _s  = this.rte.dom.indexOf($(cells[0]).parent('tr').get(0));
			var _e  = this.rte.dom.indexOf($(cells[1]).parent('tr').get(0));
			var ro  = Math.min(_s, _e); // row offset
			var rl  = Math.max(_s, _e) - ro + 1; // row length
			var _c1 = this.rte.dom.tableColumn(cells[0], true, true); 
			var _c2 = this.rte.dom.tableColumn(cells[1], true);
			var _i1 = $.inArray(cells[0], _c1.column); 
			var _i2 = $.inArray(cells[1], _c2.column);
			
			var colBegin = _c1.info.offset[_i1] < _c2.info.offset[_i2]  ? _c1 : _c2;
			var colEnd   = _c1.info.offset[_i1] >= _c2.info.offset[_i2] ? _c1 : _c2;
			var length   = 0;
			var target   = null;
			var html     = '';

			var rows = $($(cells[0]).parents('table').eq(0).find('tr').get().slice(ro, ro+rl))
				.each( function(i) {
					var _l = html.length;
					var accept = false;
					$(this).children('td,th').each(function() {
						var $this   = $(this);
						var inBegin = $.inArray(this, colBegin.column);
						var inEnd   = $.inArray(this, colEnd.column);
						
						if (inBegin!=-1 || inEnd!=-1) {
							accept = inBegin!=-1 && inEnd==-1;
							var len = parseInt($this.attr('colspan')||1)
							if (i == 0) {
								length += len;
							}
							
							if (inBegin!=-1 && i>0) {
								var delta = colBegin.info.delta[inBegin];
								if (delta>0) {
									if ($this.css('text-align') == 'left') {
										var cell = $this.clone(true);
										$this.html('&nbsp;');
									} else {
										var cell = $this.clone().html('&nbsp;');
									}
									cell.removeAttr('colspan').removeAttr('id').insertBefore(this);
									if (delta>1) {
										cell.attr('colspan', delta);
									}
								}
							}
							
							if (inEnd!=-1) {
								var delta = colEnd.info.delta[inEnd];
								if (len-delta>1) {
									var cp = len-delta-1;
									if ($this.css('text-align') == 'right') {
										var cell = $this.clone(true);
										$this.html('&nbsp;');
									} else {
										var cell = $this.clone().html('&nbsp;');
									}
									cell.removeAttr('colspan').removeAttr('id').insertAfter(this);
									if (cp>1) {
										cell.attr('colspan', cp);
									}
								}
							}
							if (!target) {
								target = $this;
							} else {
								html += $this.html();
								$this.remove();
							}
						} else if (accept) {
							if (i == 0) {
								length += parseInt($this.attr('colspan')||1);
							}
							html += $this.html();
							$this.remove();
							

						}
					})
					html += _l!=html.length ? '<br />' : '';
				});

			target.removeAttr('colspan').removeAttr('rowspan').html(target.html()+html)
			if (length>1) {
				target.attr('colspan', length);
			}
			if (rl>1) {
				target.attr('rowspan', rl);
			}
			// sometimes when merge cells with different rowspans we get "lost" cells in rows 
			// this add cells if needed
			this.rte.dom.fixTable($(cells[0]).parents('table').get(0));
		}
	}
	
	this.update = function() {
		if (selectedCells()) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

/**
 * @class button - split merged cell
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸
 * @todo split not merged cell 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru 
 **/
elRTE.prototype.ui.prototype.buttons.tbcellsplit = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	
	this.command = function() {
		var n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(TD|TH)$/);
		if (n) {
			var colspan = parseInt(this.rte.dom.attr(n, 'colspan'));
			var rowspan = parseInt(this.rte.dom.attr(n, 'rowspan'));
			if (colspan>1 || rowspan>1) {
				var cnum = colspan-1;
				var rnum = rowspan-1;
				var tb   = this.rte.dom.parent(n, /^TABLE$/);
				var tbm  = this.rte.dom.tableMatrix(tb);
				
				// ÑÑ‡ÐµÐ¹ÐºÐ¸ Ð² Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¼ Ñ€ÑÐ´Ñƒ
				if (cnum) {
					for (var i=0; i<cnum; i++) {
						$(this.rte.dom.create(n.nodeName)).html('&nbsp;').insertAfter(n);
					}
				}
				if (rnum) {
					var ndx  = this.rte.dom.indexesOfCell(n, tbm)
					var rndx = ndx[0];
					var cndx = ndx[1];
					// ÑÑ‡ÐµÐ¹ÐºÐ¸ Ð² ÑÐ»ÐµÐ´ÑƒÑ‰Ð¸Ñ… Ñ€ÑÐ´Ð°Ñ…
					for (var r=rndx+1; r < rndx+rnum+1; r++) {
						var cell;
						
						if (!tbm[r][cndx].nodeName) {
							if (tbm[r][cndx-1].nodeName) {
								cell = tbm[r][cndx-1];
							} else {
								for (var i=cndx-1; i>=0; i--) {
									if (tbm[r][i].nodeName) {
										cell =tbm[r][i];
										break;
									}
								}
							}
							if (cell) {
								for (var i=0; i<= cnum; i++) {
									$(this.rte.dom.create(cell.nodeName)).html('&nbsp;').insertAfter(cell);
								}
							}
						}
					};
				}
				$(n).removeAttr('colspan').removeAttr('rowspan');
				this.rte.dom.fixTable(tb);
			}
		}
		this.rte.ui.update(true);
	}
	
	this.update = function() {
		var n = this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(TD|TH)$/);
		if (n && (parseInt(this.rte.dom.attr(n, 'colspan'))>1 || parseInt(this.rte.dom.attr(n, 'rowspan'))>1)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

/**
 * @class button - Insert new column in table(before or after current)
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.tbcolbefore = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	
	this.command = function() {
		var cells = this.rte.dom.tableColumn(this.rte.selection.getNode(), false, true);
		if (cells.length) {
			$.each(cells, function() {
				var $this = $(this);
				var cp = parseInt($this.attr('colspan')||1)
				if (cp >1) {
					$this.attr('colspan', cp+1);
				} else {
					var c = $this.clone().html('&nbsp;').removeAttr('colspan').removeAttr('width').removeAttr('id');
					if (self.name == 'tbcolbefore') {
						c.insertBefore(this);
					} else {
						c.insertAfter(this);
					}
				}
			});
			this.rte.ui.update();
		}
	}
	
	this.update = function() {
		if (this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(TD|TH)$/)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

elRTE.prototype.ui.prototype.buttons.tbcolafter = elRTE.prototype.ui.prototype.buttons.tbcolbefore;

/**
 * @class button - remove table colunm
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.tbcolrm = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	
	this.command = function() {
		var n     = this.rte.selection.getNode();
		var c     = this.rte.dom.selfOrParent(n, /^(TD|TH)$/);
		var prev  = $(c).prev('td,th').get(0);
		var next  = $(c).next('td,th').get(0);			
		var tb    = this.rte.dom.parent(n, /^TABLE$/);
		var cells = this.rte.dom.tableColumn(n, false, true);

		if (cells.length) {
			$.each(cells, function() {
				var $this = $(this);
				var cp    = parseInt($this.attr('colspan')||1);
				if ( cp>1 ) {
					$this.attr('colspan', cp-1);
				} else {
					$this.remove();
				}
			});
			this.rte.dom.fixTable(tb);
			if (prev || next) {
				this.rte.selection.selectContents(prev ? prev : next).collapse(true);
			}
			this.rte.ui.update(true);
		}
	}
	
	this.update = function() {
		if (this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^(TD|TH)$/)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

/**
 * @class button - remove table row
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸ 
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru
 **/
elRTE.prototype.ui.prototype.buttons.tbrowrm = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);
	var self = this;
	this.command = function() {
		
		var n  = this.rte.selection.getNode();
		var c  = this.rte.dom.selfOrParent(n, /^(TD|TH)$/);
		var r  = this.rte.dom.selfOrParent(c, /^TR$/);
		var tb = this.rte.dom.selfOrParent(c, /^TABLE$/);
		var mx = this.rte.dom.tableMatrix(tb);
		
		if (c && r && mx.length) {
			if (mx.length==1) {
				$(tb).remove();
				return this.rte.ui.update();
			}
			var mdf = [];
			var ro  = $(r).prevAll('tr').length;
			
			function _find(x, y) {
				while (y>0) {
					y--;
					if (mx[y] && mx[y][x] && mx[y][x].nodeName) {
						return mx[y][x];
					}
				}
			}
			
			// move cell with rowspan>1 to next row
			function _move(cell, x) {
				y = ro+1;
				var sibling= null;
				if (mx[y]) {
					for (var _x=0; _x<x; _x++) {
						if (mx[y][_x] && mx[y][_x].nodeName) {
							sibling = mx[y][_x];
						}
					};
					
					cell = cell.remove();
					if (sibling) {
						cell.insertAfter(sibling);
					} else {
						cell.prependTo($(r).next('tr').eq(0));
					}
				}
			}
			
			function _cursorPos(column) {
				for (var i = 0; i<column.length; i++) {
					if (column[i] == c) {
						return i<column.length-1 ? column[i+1] : column[i-1];
					}
				}
			}
			
			for (var i=0; i<mx[ro].length; i++) {
				var cell = null;
				var move = false;
				if (mx[ro][i] && mx[ro][i].nodeName) {
					cell = mx[ro][i];
					move = true;
				} else if (mx[ro][i] == '-' && (cell = _find(i, ro))) {
					move = false;
				}
				if (cell) {
					cell = $(cell);
					var rowspan = parseInt(cell.attr('rowspan')||1);
					if (rowspan>1) {
						cell.attr('rowspan', rowspan-1);
						move && _move(cell, i, ro);
					} 
				}
			};
			
			var _c = _cursorPos(this.rte.dom.tableColumn(c));
			if (_c) {
				this.rte.selection.selectContents(_c).collapse(true);
			}

			$(r).remove();
		}
		this.rte.ui.update();
	}
	
	this.update = function() {
		if (this.rte.dom.selfOrParent(this.rte.selection.getNode(), /^TR$/)) {
			this.domElem.removeClass('disabled');
		} else {
			this.domElem.addClass('disabled');
		}
	}
}

/**
 * @class button - remove link
 *
 * @param  elRTE  rte   Ð¾Ð±ÑŠÐµÐºÑ‚-Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¾Ñ€
 * @param  String name  Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ ÐºÐ½Ð¾Ð¿ÐºÐ¸
 *
 * @author:    Dmitry Levashov (dio) dio@std42.ru
 * @copyright: Studio 42, http://www.std42.ru 
 **/
elRTE.prototype.ui.prototype.buttons.unlink = function(rte, name) {
	this.constructor.prototype.constructor.call(this, rte, name);

	this.command = function() {
		var n = this.rte.selection.getNode();
		var l, link;
		if ((l = this.rte.dom.selfOrParentLink(n))) {
			link = l;
		} else if ((l = this.rte.dom.childLinks(n))) {
			link = l[0];
		}
		if (link) {
			this.rte.selection.select(link);
			this.rte.doc.execCommand('unlink', false, null);
			this.rte.ui.update(true);
		}
		
	}
	
	this.update = function() {
		var n = this.rte.selection.getNode();
		if (this.rte.dom.selfOrParentLink(n) || this.rte.dom.childLinks(n).length) {
			this.domElem.removeClass('disabled').addClass('active');
		} else {
			this.domElem.removeClass('active').addClass('disabled');
		}
	}
}


